├── LICENSE
├── README.md
├── Resources
├── Compass.png
├── Compass.pxm
├── Overlay.jpg
├── Overlay.pxm
├── POI.jpg
├── Search.png
├── Search.pxm
├── Search@2x.png
└── Search@3x.png
├── Screenshots
├── TGLAugmentedRealityExample.jpg
└── TGLAugmentedRealityExample2.jpg
├── TGLAugmentedRealityExample.xcodeproj
├── project.pbxproj
├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcuserdata
│ │ └── mac.xcuserdatad
│ │ └── UserInterfaceState.xcuserstate
└── xcuserdata
│ └── mac.xcuserdatad
│ └── xcschemes
│ └── xcschememanagement.plist
├── TGLAugmentedRealityExample
├── AppDelegate.h
├── AppDelegate.m
├── Assets.xcassets
│ ├── AppIcon.appiconset
│ │ └── Contents.json
│ ├── Contents.json
│ ├── Locations.imageset
│ │ ├── Contents.json
│ │ ├── Locations.png
│ │ ├── Locations@2x.png
│ │ └── Locations@3x.png
│ └── Pin.imageset
│ │ ├── Contents.json
│ │ ├── Pin.png
│ │ ├── Pin@2x.png
│ │ └── Pin@3x.png
├── AugmentedViewController.h
├── AugmentedViewController.m
├── Base.lproj
│ ├── LaunchScreen.storyboard
│ └── Main.storyboard
├── Compass.png
├── Info.plist
├── PlaceOfInterest.h
├── PlaceOfInterest.m
├── PlaceOfInterestView.h
├── PlaceOfInterestView.m
├── SearchViewController.h
├── SearchViewController.m
├── Target.png
├── de.lproj
│ ├── InfoPlist.strings
│ └── Localizable.strings
├── en.lproj
│ ├── InfoPlist.strings
│ ├── Localizable.strings
│ └── Main.strings
└── main.m
├── TGLAugmentedRealityView.podspec
└── TGLAugmentedRealityView
├── TGLARBillboardImageShape.h
├── TGLARBillboardImageShape.m
├── TGLARCompass.h
├── TGLARCompassView.h
├── TGLARCompassView.m
├── TGLARImageShape.h
├── TGLARImageShape.m
├── TGLAROverlay.h
├── TGLAROverlayContainerView.h
├── TGLAROverlayContainerView.m
├── TGLARShapeOverlay.h
├── TGLARShapeOverlay.m
├── TGLARView.h
├── TGLARView.m
├── TGLARViewOverlay.h
└── TGLARViewOverlay.m
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015-2017 PowerMobile Team (https://powermobile.org)
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
13 | all 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
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](http://cocoadocs.org/docsets/TGLAugmentedRealityView)
2 | [](https://github.com/powermobileteam/TGLAugmentedRealityView/tags)
3 | [](https://cocoapods.org)
4 | [](https://opensource.org/licenses/MIT)
5 | [](https://cocoapods.org/pods/TGLAugmentedRealityView)
6 |
7 | TGLAugmentedRealityView
8 | =======================
9 |
10 | Place overlays on a camera preview and adjust their position depending on device attitude.
11 |
12 |
13 |
14 |
15 |
16 | Getting Started
17 | ===============
18 |
19 | Take a look at sample project `TGLAugmentedRealityExample.xcodeproj`.
20 |
21 | Usage
22 | =====
23 |
24 | Via [CocoaPods](http://cocoapods.org):
25 |
26 | * Add `pod 'TGLAugmentedRealityView', '~> 1.0'` to your project's `Podfile`
27 |
28 | Or the "classic" way:
29 |
30 | * Add files in folder `TGLAugmentedRealityView` to your project
31 |
32 | Then in your project:
33 |
34 | * Implement `TGLARViewDataSource` and `TGLARViewDelegate` protocols
35 | * Place a `TGLARView` in your storyboard and set its `-dataSource` and `-delegate` outlets
36 |
37 | Optionally:
38 |
39 | * Implement `TGLARCompass` protocol
40 | * Set `TGLARView`'s `-compass` outlet
41 |
42 | Sample
43 | ======
44 |
45 | Build and run `TGLAugmentedRealityExample.xcodeproj` on an iDevice. The app will run on the
46 | simulator, too, but will give you no camera image or device orientation.
47 |
48 | Tap the blue button and search for POIs in the map view. When finished, close the map to
49 | reveal the AR view. The POIs will be displayed with a callout and a billboard image.
50 |
51 | On the AR view use a horizontal pan gesture to adjust the compass heading and a pinch
52 | gesture to adjust the zoom factor, if available in the active video format.
53 |
54 | Requirements
55 | ============
56 |
57 | * ARC
58 | * iOS >= 9.3
59 | * Xcode 9
60 |
61 | License
62 | =======
63 |
64 | TGLAugmentedRealityView is available under the MIT License (MIT)
65 |
66 | Copyright (c) 2015-2017 PowerMobile Team (http://powermobile.org)
67 |
68 | Permission is hereby granted, free of charge, to any person obtaining a copy
69 | of this software and associated documentation files (the "Software"), to deal
70 | in the Software without restriction, including without limitation the rights
71 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
72 | copies of the Software, and to permit persons to whom the Software is
73 | furnished to do so, subject to the following conditions:
74 |
75 | The above copyright notice and this permission notice shall be included in
76 | all copies or substantial portions of the Software.
77 |
78 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
79 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
80 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
81 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
82 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
83 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
84 | THE SOFTWARE.
85 |
--------------------------------------------------------------------------------
/Resources/Compass.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powermobileweb/TGLAugmentedRealityView/2360fc88458f2d07dd9d7412c0e5e25dcf45d9ce/Resources/Compass.png
--------------------------------------------------------------------------------
/Resources/Compass.pxm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powermobileweb/TGLAugmentedRealityView/2360fc88458f2d07dd9d7412c0e5e25dcf45d9ce/Resources/Compass.pxm
--------------------------------------------------------------------------------
/Resources/Overlay.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powermobileweb/TGLAugmentedRealityView/2360fc88458f2d07dd9d7412c0e5e25dcf45d9ce/Resources/Overlay.jpg
--------------------------------------------------------------------------------
/Resources/Overlay.pxm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powermobileweb/TGLAugmentedRealityView/2360fc88458f2d07dd9d7412c0e5e25dcf45d9ce/Resources/Overlay.pxm
--------------------------------------------------------------------------------
/Resources/POI.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powermobileweb/TGLAugmentedRealityView/2360fc88458f2d07dd9d7412c0e5e25dcf45d9ce/Resources/POI.jpg
--------------------------------------------------------------------------------
/Resources/Search.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powermobileweb/TGLAugmentedRealityView/2360fc88458f2d07dd9d7412c0e5e25dcf45d9ce/Resources/Search.png
--------------------------------------------------------------------------------
/Resources/Search.pxm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powermobileweb/TGLAugmentedRealityView/2360fc88458f2d07dd9d7412c0e5e25dcf45d9ce/Resources/Search.pxm
--------------------------------------------------------------------------------
/Resources/Search@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powermobileweb/TGLAugmentedRealityView/2360fc88458f2d07dd9d7412c0e5e25dcf45d9ce/Resources/Search@2x.png
--------------------------------------------------------------------------------
/Resources/Search@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powermobileweb/TGLAugmentedRealityView/2360fc88458f2d07dd9d7412c0e5e25dcf45d9ce/Resources/Search@3x.png
--------------------------------------------------------------------------------
/Screenshots/TGLAugmentedRealityExample.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powermobileweb/TGLAugmentedRealityView/2360fc88458f2d07dd9d7412c0e5e25dcf45d9ce/Screenshots/TGLAugmentedRealityExample.jpg
--------------------------------------------------------------------------------
/Screenshots/TGLAugmentedRealityExample2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powermobileweb/TGLAugmentedRealityView/2360fc88458f2d07dd9d7412c0e5e25dcf45d9ce/Screenshots/TGLAugmentedRealityExample2.jpg
--------------------------------------------------------------------------------
/TGLAugmentedRealityExample.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 3D0E46571C071533003CBE4F /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 3D0E46551C071533003CBE4F /* Localizable.strings */; };
11 | 3D0E465B1C0717EC003CBE4F /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 3D0E465D1C0717EC003CBE4F /* InfoPlist.strings */; };
12 | 3D0E465F1C071950003CBE4F /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 3D0E46611C071950003CBE4F /* LaunchScreen.storyboard */; };
13 | 3D701EE51BFF53410092DB4B /* PlaceOfInterestView.m in Sources */ = {isa = PBXBuildFile; fileRef = 3D701EE41BFF53410092DB4B /* PlaceOfInterestView.m */; };
14 | 3D7AD0AF1BF0BDD300EB040C /* PlaceOfInterest.m in Sources */ = {isa = PBXBuildFile; fileRef = 3D7AD0AE1BF0BDD300EB040C /* PlaceOfInterest.m */; };
15 | 3D7DF1761FEBBAA1009346C6 /* Compass.png in Resources */ = {isa = PBXBuildFile; fileRef = 3D7DF1751FEBBAA0009346C6 /* Compass.png */; };
16 | 3D7DF1781FEC04F9009346C6 /* Target.png in Resources */ = {isa = PBXBuildFile; fileRef = 3D7DF1771FEC04F8009346C6 /* Target.png */; };
17 | 3D8A19411C060FED00B91862 /* TGLARBillboardImageShape.m in Sources */ = {isa = PBXBuildFile; fileRef = 3D8A19331C060FED00B91862 /* TGLARBillboardImageShape.m */; };
18 | 3D8A19421C060FED00B91862 /* TGLARCompassView.m in Sources */ = {isa = PBXBuildFile; fileRef = 3D8A19351C060FED00B91862 /* TGLARCompassView.m */; };
19 | 3D8A19431C060FED00B91862 /* TGLARImageShape.m in Sources */ = {isa = PBXBuildFile; fileRef = 3D8A19371C060FED00B91862 /* TGLARImageShape.m */; };
20 | 3D8A19441C060FED00B91862 /* TGLAROverlayContainerView.m in Sources */ = {isa = PBXBuildFile; fileRef = 3D8A193A1C060FED00B91862 /* TGLAROverlayContainerView.m */; };
21 | 3D8A19451C060FED00B91862 /* TGLARShapeOverlay.m in Sources */ = {isa = PBXBuildFile; fileRef = 3D8A193C1C060FED00B91862 /* TGLARShapeOverlay.m */; };
22 | 3D8A19461C060FED00B91862 /* TGLARView.m in Sources */ = {isa = PBXBuildFile; fileRef = 3D8A193E1C060FED00B91862 /* TGLARView.m */; };
23 | 3D8A19471C060FED00B91862 /* TGLARViewOverlay.m in Sources */ = {isa = PBXBuildFile; fileRef = 3D8A19401C060FED00B91862 /* TGLARViewOverlay.m */; };
24 | 3DAEF8671BF0954C0037E9C4 /* AugmentedViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3DAEF8611BF0954C0037E9C4 /* AugmentedViewController.m */; };
25 | 3DCE74C81BECB2E800985E03 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 3DCE74C71BECB2E800985E03 /* main.m */; };
26 | 3DCE74CB1BECB2E800985E03 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 3DCE74CA1BECB2E800985E03 /* AppDelegate.m */; };
27 | 3DCE74CE1BECB2E800985E03 /* SearchViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3DCE74CD1BECB2E800985E03 /* SearchViewController.m */; };
28 | 3DCE74D11BECB2E800985E03 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 3DCE74CF1BECB2E800985E03 /* Main.storyboard */; };
29 | 3DCE74D31BECB2E800985E03 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 3DCE74D21BECB2E800985E03 /* Assets.xcassets */; };
30 | 3DCE74DE1BECB30400985E03 /* MapKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3DCE74DD1BECB30400985E03 /* MapKit.framework */; };
31 | /* End PBXBuildFile section */
32 |
33 | /* Begin PBXFileReference section */
34 | 3D0E46501C06FF0F003CBE4F /* TGLARCompass.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGLARCompass.h; sourceTree = ""; };
35 | 3D0E46711C071C11003CBE4F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
36 | 3D0E46721C071C11003CBE4F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
37 | 3D0E46731C071C51003CBE4F /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Main.strings; sourceTree = ""; };
38 | 3D0E46751C071C6C003CBE4F /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; };
39 | 3D0E46761C071C76003CBE4F /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; };
40 | 3D0E46791C071E01003CBE4F /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Localizable.strings; sourceTree = ""; };
41 | 3D0E467A1C071E06003CBE4F /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/InfoPlist.strings; sourceTree = ""; };
42 | 3D701EE31BFF53410092DB4B /* PlaceOfInterestView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PlaceOfInterestView.h; sourceTree = ""; };
43 | 3D701EE41BFF53410092DB4B /* PlaceOfInterestView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PlaceOfInterestView.m; sourceTree = ""; };
44 | 3D7AD0AD1BF0BDD300EB040C /* PlaceOfInterest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PlaceOfInterest.h; sourceTree = ""; };
45 | 3D7AD0AE1BF0BDD300EB040C /* PlaceOfInterest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PlaceOfInterest.m; sourceTree = ""; };
46 | 3D7DF1751FEBBAA0009346C6 /* Compass.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Compass.png; sourceTree = ""; };
47 | 3D7DF1771FEC04F8009346C6 /* Target.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Target.png; sourceTree = ""; };
48 | 3D8A19321C060FED00B91862 /* TGLARBillboardImageShape.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGLARBillboardImageShape.h; sourceTree = ""; };
49 | 3D8A19331C060FED00B91862 /* TGLARBillboardImageShape.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGLARBillboardImageShape.m; sourceTree = ""; };
50 | 3D8A19341C060FED00B91862 /* TGLARCompassView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGLARCompassView.h; sourceTree = ""; };
51 | 3D8A19351C060FED00B91862 /* TGLARCompassView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGLARCompassView.m; sourceTree = ""; };
52 | 3D8A19361C060FED00B91862 /* TGLARImageShape.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGLARImageShape.h; sourceTree = ""; };
53 | 3D8A19371C060FED00B91862 /* TGLARImageShape.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGLARImageShape.m; sourceTree = ""; };
54 | 3D8A19381C060FED00B91862 /* TGLAROverlay.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGLAROverlay.h; sourceTree = ""; };
55 | 3D8A19391C060FED00B91862 /* TGLAROverlayContainerView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGLAROverlayContainerView.h; sourceTree = ""; };
56 | 3D8A193A1C060FED00B91862 /* TGLAROverlayContainerView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGLAROverlayContainerView.m; sourceTree = ""; };
57 | 3D8A193B1C060FED00B91862 /* TGLARShapeOverlay.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGLARShapeOverlay.h; sourceTree = ""; };
58 | 3D8A193C1C060FED00B91862 /* TGLARShapeOverlay.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGLARShapeOverlay.m; sourceTree = ""; };
59 | 3D8A193D1C060FED00B91862 /* TGLARView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGLARView.h; sourceTree = ""; };
60 | 3D8A193E1C060FED00B91862 /* TGLARView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGLARView.m; sourceTree = ""; };
61 | 3D8A193F1C060FED00B91862 /* TGLARViewOverlay.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGLARViewOverlay.h; sourceTree = ""; };
62 | 3D8A19401C060FED00B91862 /* TGLARViewOverlay.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGLARViewOverlay.m; sourceTree = ""; };
63 | 3DAEF8601BF0954C0037E9C4 /* AugmentedViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AugmentedViewController.h; sourceTree = ""; };
64 | 3DAEF8611BF0954C0037E9C4 /* AugmentedViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AugmentedViewController.m; sourceTree = ""; };
65 | 3DCE74C31BECB2E800985E03 /* TGLARViewExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TGLARViewExample.app; sourceTree = BUILT_PRODUCTS_DIR; };
66 | 3DCE74C71BECB2E800985E03 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; };
67 | 3DCE74C91BECB2E800985E03 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; };
68 | 3DCE74CA1BECB2E800985E03 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; };
69 | 3DCE74CC1BECB2E800985E03 /* SearchViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SearchViewController.h; sourceTree = ""; };
70 | 3DCE74CD1BECB2E800985E03 /* SearchViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SearchViewController.m; sourceTree = ""; };
71 | 3DCE74D21BECB2E800985E03 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
72 | 3DCE74D71BECB2E800985E03 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
73 | 3DCE74DD1BECB30400985E03 /* MapKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MapKit.framework; path = System/Library/Frameworks/MapKit.framework; sourceTree = SDKROOT; };
74 | /* End PBXFileReference section */
75 |
76 | /* Begin PBXFrameworksBuildPhase section */
77 | 3DCE74C01BECB2E800985E03 /* Frameworks */ = {
78 | isa = PBXFrameworksBuildPhase;
79 | buildActionMask = 2147483647;
80 | files = (
81 | 3DCE74DE1BECB30400985E03 /* MapKit.framework in Frameworks */,
82 | );
83 | runOnlyForDeploymentPostprocessing = 0;
84 | };
85 | /* End PBXFrameworksBuildPhase section */
86 |
87 | /* Begin PBXGroup section */
88 | 3D8A19311C060FED00B91862 /* TGLAugmentedRealityView */ = {
89 | isa = PBXGroup;
90 | children = (
91 | 3D8A19321C060FED00B91862 /* TGLARBillboardImageShape.h */,
92 | 3D8A19331C060FED00B91862 /* TGLARBillboardImageShape.m */,
93 | 3D0E46501C06FF0F003CBE4F /* TGLARCompass.h */,
94 | 3D8A19341C060FED00B91862 /* TGLARCompassView.h */,
95 | 3D8A19351C060FED00B91862 /* TGLARCompassView.m */,
96 | 3D8A19361C060FED00B91862 /* TGLARImageShape.h */,
97 | 3D8A19371C060FED00B91862 /* TGLARImageShape.m */,
98 | 3D8A19381C060FED00B91862 /* TGLAROverlay.h */,
99 | 3D8A19391C060FED00B91862 /* TGLAROverlayContainerView.h */,
100 | 3D8A193A1C060FED00B91862 /* TGLAROverlayContainerView.m */,
101 | 3D8A193B1C060FED00B91862 /* TGLARShapeOverlay.h */,
102 | 3D8A193C1C060FED00B91862 /* TGLARShapeOverlay.m */,
103 | 3D8A193D1C060FED00B91862 /* TGLARView.h */,
104 | 3D8A193E1C060FED00B91862 /* TGLARView.m */,
105 | 3D8A193F1C060FED00B91862 /* TGLARViewOverlay.h */,
106 | 3D8A19401C060FED00B91862 /* TGLARViewOverlay.m */,
107 | );
108 | path = TGLAugmentedRealityView;
109 | sourceTree = "";
110 | };
111 | 3DAEF8551BF0952E0037E9C4 /* Resources */ = {
112 | isa = PBXGroup;
113 | children = (
114 | 3D7DF1771FEC04F8009346C6 /* Target.png */,
115 | 3D7DF1751FEBBAA0009346C6 /* Compass.png */,
116 | 3DCE74CF1BECB2E800985E03 /* Main.storyboard */,
117 | 3DCE74D21BECB2E800985E03 /* Assets.xcassets */,
118 | 3D0E46611C071950003CBE4F /* LaunchScreen.storyboard */,
119 | 3D0E46551C071533003CBE4F /* Localizable.strings */,
120 | );
121 | name = Resources;
122 | sourceTree = "";
123 | };
124 | 3DCE74BA1BECB2E800985E03 = {
125 | isa = PBXGroup;
126 | children = (
127 | 3D8A19311C060FED00B91862 /* TGLAugmentedRealityView */,
128 | 3DCE74C51BECB2E800985E03 /* TGLAugmentedRealityExample */,
129 | 3DCE74C41BECB2E800985E03 /* Products */,
130 | );
131 | sourceTree = "";
132 | };
133 | 3DCE74C41BECB2E800985E03 /* Products */ = {
134 | isa = PBXGroup;
135 | children = (
136 | 3DCE74C31BECB2E800985E03 /* TGLARViewExample.app */,
137 | );
138 | name = Products;
139 | sourceTree = "";
140 | };
141 | 3DCE74C51BECB2E800985E03 /* TGLAugmentedRealityExample */ = {
142 | isa = PBXGroup;
143 | children = (
144 | 3DCE74C91BECB2E800985E03 /* AppDelegate.h */,
145 | 3DCE74CA1BECB2E800985E03 /* AppDelegate.m */,
146 | 3DAEF8601BF0954C0037E9C4 /* AugmentedViewController.h */,
147 | 3DAEF8611BF0954C0037E9C4 /* AugmentedViewController.m */,
148 | 3D7AD0AD1BF0BDD300EB040C /* PlaceOfInterest.h */,
149 | 3D7AD0AE1BF0BDD300EB040C /* PlaceOfInterest.m */,
150 | 3D701EE31BFF53410092DB4B /* PlaceOfInterestView.h */,
151 | 3D701EE41BFF53410092DB4B /* PlaceOfInterestView.m */,
152 | 3DCE74CC1BECB2E800985E03 /* SearchViewController.h */,
153 | 3DCE74CD1BECB2E800985E03 /* SearchViewController.m */,
154 | 3DCE74DF1BECB31200985E03 /* Frameworks */,
155 | 3DAEF8551BF0952E0037E9C4 /* Resources */,
156 | 3DCE74C61BECB2E800985E03 /* Supporting Files */,
157 | );
158 | path = TGLAugmentedRealityExample;
159 | sourceTree = "";
160 | };
161 | 3DCE74C61BECB2E800985E03 /* Supporting Files */ = {
162 | isa = PBXGroup;
163 | children = (
164 | 3DCE74D71BECB2E800985E03 /* Info.plist */,
165 | 3DCE74C71BECB2E800985E03 /* main.m */,
166 | 3D0E465D1C0717EC003CBE4F /* InfoPlist.strings */,
167 | );
168 | name = "Supporting Files";
169 | sourceTree = "";
170 | };
171 | 3DCE74DF1BECB31200985E03 /* Frameworks */ = {
172 | isa = PBXGroup;
173 | children = (
174 | 3DCE74DD1BECB30400985E03 /* MapKit.framework */,
175 | );
176 | name = Frameworks;
177 | path = ..;
178 | sourceTree = "";
179 | };
180 | /* End PBXGroup section */
181 |
182 | /* Begin PBXNativeTarget section */
183 | 3DCE74C21BECB2E800985E03 /* TGLAugmentedRealityExample */ = {
184 | isa = PBXNativeTarget;
185 | buildConfigurationList = 3DCE74DA1BECB2E800985E03 /* Build configuration list for PBXNativeTarget "TGLAugmentedRealityExample" */;
186 | buildPhases = (
187 | 3DCE74BF1BECB2E800985E03 /* Sources */,
188 | 3DCE74C01BECB2E800985E03 /* Frameworks */,
189 | 3DCE74C11BECB2E800985E03 /* Resources */,
190 | );
191 | buildRules = (
192 | );
193 | dependencies = (
194 | );
195 | name = TGLAugmentedRealityExample;
196 | productName = TGLAugmentedRealityExample;
197 | productReference = 3DCE74C31BECB2E800985E03 /* TGLARViewExample.app */;
198 | productType = "com.apple.product-type.application";
199 | };
200 | /* End PBXNativeTarget section */
201 |
202 | /* Begin PBXProject section */
203 | 3DCE74BB1BECB2E800985E03 /* Project object */ = {
204 | isa = PBXProject;
205 | attributes = {
206 | LastUpgradeCheck = 0920;
207 | ORGANIZATIONNAME = "Tim Gleue";
208 | TargetAttributes = {
209 | 3DCE74C21BECB2E800985E03 = {
210 | CreatedOnToolsVersion = 7.1;
211 | SystemCapabilities = {
212 | com.apple.Maps.iOS = {
213 | enabled = 1;
214 | };
215 | };
216 | };
217 | };
218 | };
219 | buildConfigurationList = 3DCE74BE1BECB2E800985E03 /* Build configuration list for PBXProject "TGLAugmentedRealityExample" */;
220 | compatibilityVersion = "Xcode 3.2";
221 | developmentRegion = English;
222 | hasScannedForEncodings = 0;
223 | knownRegions = (
224 | de,
225 | Base,
226 | en,
227 | );
228 | mainGroup = 3DCE74BA1BECB2E800985E03;
229 | productRefGroup = 3DCE74C41BECB2E800985E03 /* Products */;
230 | projectDirPath = "";
231 | projectRoot = "";
232 | targets = (
233 | 3DCE74C21BECB2E800985E03 /* TGLAugmentedRealityExample */,
234 | );
235 | };
236 | /* End PBXProject section */
237 |
238 | /* Begin PBXResourcesBuildPhase section */
239 | 3DCE74C11BECB2E800985E03 /* Resources */ = {
240 | isa = PBXResourcesBuildPhase;
241 | buildActionMask = 2147483647;
242 | files = (
243 | 3D7DF1781FEC04F9009346C6 /* Target.png in Resources */,
244 | 3D7DF1761FEBBAA1009346C6 /* Compass.png in Resources */,
245 | 3D0E465F1C071950003CBE4F /* LaunchScreen.storyboard in Resources */,
246 | 3D0E46571C071533003CBE4F /* Localizable.strings in Resources */,
247 | 3D0E465B1C0717EC003CBE4F /* InfoPlist.strings in Resources */,
248 | 3DCE74D31BECB2E800985E03 /* Assets.xcassets in Resources */,
249 | 3DCE74D11BECB2E800985E03 /* Main.storyboard in Resources */,
250 | );
251 | runOnlyForDeploymentPostprocessing = 0;
252 | };
253 | /* End PBXResourcesBuildPhase section */
254 |
255 | /* Begin PBXSourcesBuildPhase section */
256 | 3DCE74BF1BECB2E800985E03 /* Sources */ = {
257 | isa = PBXSourcesBuildPhase;
258 | buildActionMask = 2147483647;
259 | files = (
260 | 3DCE74CE1BECB2E800985E03 /* SearchViewController.m in Sources */,
261 | 3DCE74CB1BECB2E800985E03 /* AppDelegate.m in Sources */,
262 | 3D8A19421C060FED00B91862 /* TGLARCompassView.m in Sources */,
263 | 3D8A19451C060FED00B91862 /* TGLARShapeOverlay.m in Sources */,
264 | 3DAEF8671BF0954C0037E9C4 /* AugmentedViewController.m in Sources */,
265 | 3D8A19431C060FED00B91862 /* TGLARImageShape.m in Sources */,
266 | 3D8A19471C060FED00B91862 /* TGLARViewOverlay.m in Sources */,
267 | 3D8A19461C060FED00B91862 /* TGLARView.m in Sources */,
268 | 3D8A19441C060FED00B91862 /* TGLAROverlayContainerView.m in Sources */,
269 | 3D7AD0AF1BF0BDD300EB040C /* PlaceOfInterest.m in Sources */,
270 | 3DCE74C81BECB2E800985E03 /* main.m in Sources */,
271 | 3D8A19411C060FED00B91862 /* TGLARBillboardImageShape.m in Sources */,
272 | 3D701EE51BFF53410092DB4B /* PlaceOfInterestView.m in Sources */,
273 | );
274 | runOnlyForDeploymentPostprocessing = 0;
275 | };
276 | /* End PBXSourcesBuildPhase section */
277 |
278 | /* Begin PBXVariantGroup section */
279 | 3D0E46551C071533003CBE4F /* Localizable.strings */ = {
280 | isa = PBXVariantGroup;
281 | children = (
282 | 3D0E46761C071C76003CBE4F /* en */,
283 | 3D0E46791C071E01003CBE4F /* de */,
284 | );
285 | name = Localizable.strings;
286 | sourceTree = "";
287 | };
288 | 3D0E465D1C0717EC003CBE4F /* InfoPlist.strings */ = {
289 | isa = PBXVariantGroup;
290 | children = (
291 | 3D0E46751C071C6C003CBE4F /* en */,
292 | 3D0E467A1C071E06003CBE4F /* de */,
293 | );
294 | name = InfoPlist.strings;
295 | sourceTree = "";
296 | };
297 | 3D0E46611C071950003CBE4F /* LaunchScreen.storyboard */ = {
298 | isa = PBXVariantGroup;
299 | children = (
300 | 3D0E46721C071C11003CBE4F /* Base */,
301 | );
302 | name = LaunchScreen.storyboard;
303 | sourceTree = "";
304 | };
305 | 3DCE74CF1BECB2E800985E03 /* Main.storyboard */ = {
306 | isa = PBXVariantGroup;
307 | children = (
308 | 3D0E46711C071C11003CBE4F /* Base */,
309 | 3D0E46731C071C51003CBE4F /* en */,
310 | );
311 | name = Main.storyboard;
312 | sourceTree = "";
313 | };
314 | /* End PBXVariantGroup section */
315 |
316 | /* Begin XCBuildConfiguration section */
317 | 3DCE74D81BECB2E800985E03 /* Debug */ = {
318 | isa = XCBuildConfiguration;
319 | buildSettings = {
320 | ALWAYS_SEARCH_USER_PATHS = NO;
321 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
322 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
323 | CLANG_CXX_LIBRARY = "libc++";
324 | CLANG_ENABLE_MODULES = YES;
325 | CLANG_ENABLE_OBJC_ARC = YES;
326 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
327 | CLANG_WARN_BOOL_CONVERSION = YES;
328 | CLANG_WARN_COMMA = YES;
329 | CLANG_WARN_CONSTANT_CONVERSION = YES;
330 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
331 | CLANG_WARN_EMPTY_BODY = YES;
332 | CLANG_WARN_ENUM_CONVERSION = YES;
333 | CLANG_WARN_INFINITE_RECURSION = YES;
334 | CLANG_WARN_INT_CONVERSION = YES;
335 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
336 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
337 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
338 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
339 | CLANG_WARN_STRICT_PROTOTYPES = YES;
340 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
341 | CLANG_WARN_UNREACHABLE_CODE = YES;
342 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
343 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
344 | COPY_PHASE_STRIP = NO;
345 | DEBUG_INFORMATION_FORMAT = dwarf;
346 | ENABLE_STRICT_OBJC_MSGSEND = YES;
347 | ENABLE_TESTABILITY = YES;
348 | GCC_C_LANGUAGE_STANDARD = gnu99;
349 | GCC_DYNAMIC_NO_PIC = NO;
350 | GCC_NO_COMMON_BLOCKS = YES;
351 | GCC_OPTIMIZATION_LEVEL = 0;
352 | GCC_PREPROCESSOR_DEFINITIONS = (
353 | "DEBUG=1",
354 | "$(inherited)",
355 | );
356 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
357 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
358 | GCC_WARN_UNDECLARED_SELECTOR = YES;
359 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
360 | GCC_WARN_UNUSED_FUNCTION = YES;
361 | GCC_WARN_UNUSED_VARIABLE = YES;
362 | IPHONEOS_DEPLOYMENT_TARGET = 9.3;
363 | MTL_ENABLE_DEBUG_INFO = YES;
364 | ONLY_ACTIVE_ARCH = YES;
365 | SDKROOT = iphoneos;
366 | TARGETED_DEVICE_FAMILY = "1,2";
367 | };
368 | name = Debug;
369 | };
370 | 3DCE74D91BECB2E800985E03 /* Release */ = {
371 | isa = XCBuildConfiguration;
372 | buildSettings = {
373 | ALWAYS_SEARCH_USER_PATHS = NO;
374 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
375 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
376 | CLANG_CXX_LIBRARY = "libc++";
377 | CLANG_ENABLE_MODULES = YES;
378 | CLANG_ENABLE_OBJC_ARC = YES;
379 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
380 | CLANG_WARN_BOOL_CONVERSION = YES;
381 | CLANG_WARN_COMMA = YES;
382 | CLANG_WARN_CONSTANT_CONVERSION = YES;
383 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
384 | CLANG_WARN_EMPTY_BODY = YES;
385 | CLANG_WARN_ENUM_CONVERSION = YES;
386 | CLANG_WARN_INFINITE_RECURSION = YES;
387 | CLANG_WARN_INT_CONVERSION = YES;
388 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
389 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
390 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
391 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
392 | CLANG_WARN_STRICT_PROTOTYPES = YES;
393 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
394 | CLANG_WARN_UNREACHABLE_CODE = YES;
395 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
396 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
397 | COPY_PHASE_STRIP = NO;
398 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
399 | ENABLE_NS_ASSERTIONS = NO;
400 | ENABLE_STRICT_OBJC_MSGSEND = YES;
401 | GCC_C_LANGUAGE_STANDARD = gnu99;
402 | GCC_NO_COMMON_BLOCKS = YES;
403 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
404 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
405 | GCC_WARN_UNDECLARED_SELECTOR = YES;
406 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
407 | GCC_WARN_UNUSED_FUNCTION = YES;
408 | GCC_WARN_UNUSED_VARIABLE = YES;
409 | IPHONEOS_DEPLOYMENT_TARGET = 9.3;
410 | MTL_ENABLE_DEBUG_INFO = NO;
411 | SDKROOT = iphoneos;
412 | TARGETED_DEVICE_FAMILY = "1,2";
413 | VALIDATE_PRODUCT = YES;
414 | };
415 | name = Release;
416 | };
417 | 3DCE74DB1BECB2E800985E03 /* Debug */ = {
418 | isa = XCBuildConfiguration;
419 | buildSettings = {
420 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
421 | INFOPLIST_FILE = TGLAugmentedRealityExample/Info.plist;
422 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
423 | PRODUCT_BUNDLE_IDENTIFIER = "com.gleue-interactive.TGLAugmentedRealityExample";
424 | PRODUCT_NAME = TGLARViewExample;
425 | };
426 | name = Debug;
427 | };
428 | 3DCE74DC1BECB2E800985E03 /* Release */ = {
429 | isa = XCBuildConfiguration;
430 | buildSettings = {
431 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
432 | INFOPLIST_FILE = TGLAugmentedRealityExample/Info.plist;
433 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
434 | PRODUCT_BUNDLE_IDENTIFIER = "com.gleue-interactive.TGLAugmentedRealityExample";
435 | PRODUCT_NAME = TGLARViewExample;
436 | };
437 | name = Release;
438 | };
439 | /* End XCBuildConfiguration section */
440 |
441 | /* Begin XCConfigurationList section */
442 | 3DCE74BE1BECB2E800985E03 /* Build configuration list for PBXProject "TGLAugmentedRealityExample" */ = {
443 | isa = XCConfigurationList;
444 | buildConfigurations = (
445 | 3DCE74D81BECB2E800985E03 /* Debug */,
446 | 3DCE74D91BECB2E800985E03 /* Release */,
447 | );
448 | defaultConfigurationIsVisible = 0;
449 | defaultConfigurationName = Release;
450 | };
451 | 3DCE74DA1BECB2E800985E03 /* Build configuration list for PBXNativeTarget "TGLAugmentedRealityExample" */ = {
452 | isa = XCConfigurationList;
453 | buildConfigurations = (
454 | 3DCE74DB1BECB2E800985E03 /* Debug */,
455 | 3DCE74DC1BECB2E800985E03 /* Release */,
456 | );
457 | defaultConfigurationIsVisible = 0;
458 | defaultConfigurationName = Release;
459 | };
460 | /* End XCConfigurationList section */
461 | };
462 | rootObject = 3DCE74BB1BECB2E800985E03 /* Project object */;
463 | }
464 |
--------------------------------------------------------------------------------
/TGLAugmentedRealityExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/TGLAugmentedRealityExample.xcodeproj/project.xcworkspace/xcuserdata/mac.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powermobileweb/TGLAugmentedRealityView/2360fc88458f2d07dd9d7412c0e5e25dcf45d9ce/TGLAugmentedRealityExample.xcodeproj/project.xcworkspace/xcuserdata/mac.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/TGLAugmentedRealityExample.xcodeproj/xcuserdata/mac.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | TGLAugmentedRealityExample.xcscheme
8 |
9 | orderHint
10 | 0
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/TGLAugmentedRealityExample/AppDelegate.h:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.h
3 | // TGLAugmentedRealityExample
4 | //
5 | // Created by Tim Gleue on 06.11.15.
6 | // Copyright (c) 2015 PowerMobile Team (https://powermobile.org)
7 | //
8 | // Permission is hereby granted, free of charge, to any person obtaining a copy
9 | // of this software and associated documentation files (the "Software"), to deal
10 | // in the Software without restriction, including without limitation the rights
11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 | // copies of the Software, and to permit persons to whom the Software is
13 | // furnished to do so, subject to the following conditions:
14 | //
15 | // The above copyright notice and this permission notice shall be included in
16 | // all copies or substantial portions of the Software.
17 | //
18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 | // THE SOFTWARE.
25 |
26 | #import
27 |
28 | @interface AppDelegate : UIResponder
29 |
30 | @property (strong, nonatomic) UIWindow *window;
31 |
32 |
33 | @end
34 |
35 |
--------------------------------------------------------------------------------
/TGLAugmentedRealityExample/AppDelegate.m:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.m
3 | // TGLAugmentedRealityExample
4 | //
5 | // Created by Tim Gleue on 06.11.15.
6 | // Copyright (c) 2015 PowerMobile Team (https://powermobile.org)
7 | //
8 | // Permission is hereby granted, free of charge, to any person obtaining a copy
9 | // of this software and associated documentation files (the "Software"), to deal
10 | // in the Software without restriction, including without limitation the rights
11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 | // copies of the Software, and to permit persons to whom the Software is
13 | // furnished to do so, subject to the following conditions:
14 | //
15 | // The above copyright notice and this permission notice shall be included in
16 | // all copies or substantial portions of the Software.
17 | //
18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 | // THE SOFTWARE.
25 |
26 | #import "AppDelegate.h"
27 |
28 | @interface AppDelegate ()
29 |
30 | @end
31 |
32 | @implementation AppDelegate
33 |
34 |
35 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
36 | // Override point for customization after application launch.
37 | return YES;
38 | }
39 |
40 | - (void)applicationWillResignActive:(UIApplication *)application {
41 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
42 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
43 | }
44 |
45 | - (void)applicationDidEnterBackground:(UIApplication *)application {
46 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
47 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
48 | }
49 |
50 | - (void)applicationWillEnterForeground:(UIApplication *)application {
51 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
52 | }
53 |
54 | - (void)applicationDidBecomeActive:(UIApplication *)application {
55 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
56 | }
57 |
58 | - (void)applicationWillTerminate:(UIApplication *)application {
59 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
60 | }
61 |
62 | @end
63 |
--------------------------------------------------------------------------------
/TGLAugmentedRealityExample/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "20x20",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "20x20",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "29x29",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "29x29",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "40x40",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "40x40",
31 | "scale" : "3x"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "size" : "60x60",
36 | "scale" : "2x"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "size" : "60x60",
41 | "scale" : "3x"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "size" : "20x20",
46 | "scale" : "1x"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "size" : "20x20",
51 | "scale" : "2x"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "size" : "29x29",
56 | "scale" : "1x"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "size" : "29x29",
61 | "scale" : "2x"
62 | },
63 | {
64 | "idiom" : "ipad",
65 | "size" : "40x40",
66 | "scale" : "1x"
67 | },
68 | {
69 | "idiom" : "ipad",
70 | "size" : "40x40",
71 | "scale" : "2x"
72 | },
73 | {
74 | "idiom" : "ipad",
75 | "size" : "76x76",
76 | "scale" : "1x"
77 | },
78 | {
79 | "idiom" : "ipad",
80 | "size" : "76x76",
81 | "scale" : "2x"
82 | },
83 | {
84 | "idiom" : "ipad",
85 | "size" : "83.5x83.5",
86 | "scale" : "2x"
87 | },
88 | {
89 | "idiom" : "ios-marketing",
90 | "size" : "1024x1024",
91 | "scale" : "1x"
92 | }
93 | ],
94 | "info" : {
95 | "version" : 1,
96 | "author" : "xcode"
97 | }
98 | }
--------------------------------------------------------------------------------
/TGLAugmentedRealityExample/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/TGLAugmentedRealityExample/Assets.xcassets/Locations.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "Locations.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "Locations@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "Locations@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | },
23 | "properties" : {
24 | "template-rendering-intent" : "template"
25 | }
26 | }
--------------------------------------------------------------------------------
/TGLAugmentedRealityExample/Assets.xcassets/Locations.imageset/Locations.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powermobileweb/TGLAugmentedRealityView/2360fc88458f2d07dd9d7412c0e5e25dcf45d9ce/TGLAugmentedRealityExample/Assets.xcassets/Locations.imageset/Locations.png
--------------------------------------------------------------------------------
/TGLAugmentedRealityExample/Assets.xcassets/Locations.imageset/Locations@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powermobileweb/TGLAugmentedRealityView/2360fc88458f2d07dd9d7412c0e5e25dcf45d9ce/TGLAugmentedRealityExample/Assets.xcassets/Locations.imageset/Locations@2x.png
--------------------------------------------------------------------------------
/TGLAugmentedRealityExample/Assets.xcassets/Locations.imageset/Locations@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powermobileweb/TGLAugmentedRealityView/2360fc88458f2d07dd9d7412c0e5e25dcf45d9ce/TGLAugmentedRealityExample/Assets.xcassets/Locations.imageset/Locations@3x.png
--------------------------------------------------------------------------------
/TGLAugmentedRealityExample/Assets.xcassets/Pin.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "Pin.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "Pin@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "Pin@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/TGLAugmentedRealityExample/Assets.xcassets/Pin.imageset/Pin.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powermobileweb/TGLAugmentedRealityView/2360fc88458f2d07dd9d7412c0e5e25dcf45d9ce/TGLAugmentedRealityExample/Assets.xcassets/Pin.imageset/Pin.png
--------------------------------------------------------------------------------
/TGLAugmentedRealityExample/Assets.xcassets/Pin.imageset/Pin@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powermobileweb/TGLAugmentedRealityView/2360fc88458f2d07dd9d7412c0e5e25dcf45d9ce/TGLAugmentedRealityExample/Assets.xcassets/Pin.imageset/Pin@2x.png
--------------------------------------------------------------------------------
/TGLAugmentedRealityExample/Assets.xcassets/Pin.imageset/Pin@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powermobileweb/TGLAugmentedRealityView/2360fc88458f2d07dd9d7412c0e5e25dcf45d9ce/TGLAugmentedRealityExample/Assets.xcassets/Pin.imageset/Pin@3x.png
--------------------------------------------------------------------------------
/TGLAugmentedRealityExample/AugmentedViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // AugmentedViewController.h
3 | // TGLAugmentedRealityExample
4 | //
5 | // Created by Tim Gleue on 09.11.15.
6 | // Copyright (c) 2015 PowerMobile Team (https://powermobile.org)
7 | //
8 | // Permission is hereby granted, free of charge, to any person obtaining a copy
9 | // of this software and associated documentation files (the "Software"), to deal
10 | // in the Software without restriction, including without limitation the rights
11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 | // copies of the Software, and to permit persons to whom the Software is
13 | // furnished to do so, subject to the following conditions:
14 | //
15 | // The above copyright notice and this permission notice shall be included in
16 | // all copies or substantial portions of the Software.
17 | //
18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 | // THE SOFTWARE.
25 |
26 | #import
27 |
28 | #import
29 |
30 | #import "PlaceOfInterest.h"
31 |
32 | @interface AugmentedViewController : UIViewController
33 |
34 | @property (nonatomic, assign) float userHeight;
35 | @property (nonatomic, strong) CLLocation *userLocation;
36 |
37 | @property (nonatomic, strong) NSArray *places;
38 |
39 | @end
40 |
--------------------------------------------------------------------------------
/TGLAugmentedRealityExample/AugmentedViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // AugmentedViewController.m
3 | // TGLAugmentedRealityExample
4 | //
5 | // Created by Tim Gleue on 09.11.15.
6 | // Copyright (c) 2015 PowerMobile Team (https://powermobile.org)
7 | //
8 | // Permission is hereby granted, free of charge, to any person obtaining a copy
9 | // of this software and associated documentation files (the "Software"), to deal
10 | // in the Software without restriction, including without limitation the rights
11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 | // copies of the Software, and to permit persons to whom the Software is
13 | // furnished to do so, subject to the following conditions:
14 | //
15 | // The above copyright notice and this permission notice shall be included in
16 | // all copies or substantial portions of the Software.
17 | //
18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 | // THE SOFTWARE.
25 |
26 | #import "AugmentedViewController.h"
27 | #import "SearchViewController.h"
28 |
29 | #import "TGLARView.h"
30 | #import "TGLARImageShape.h"
31 | #import "TGLARBillboardImageShape.h"
32 |
33 | #import "PlaceOfInterestView.h"
34 |
35 | #import
36 |
37 | @interface AugmentedViewController ()
38 |
39 | @property (weak, nonatomic) IBOutlet TGLARView *arView;
40 | @property (weak, nonatomic) IBOutlet UIButton *northButton;
41 |
42 | @property (nonatomic, strong) CLLocationManager *locationManager;
43 |
44 | @property (nonatomic, strong) PlaceOfInterest *userLocationPOI;
45 |
46 | @end
47 |
48 | @implementation AugmentedViewController
49 |
50 | #pragma mark - View lifecycle
51 |
52 | - (void)dealloc {
53 |
54 | self.places = nil;
55 | self.userLocationPOI = nil;
56 | }
57 |
58 | - (void)viewDidLoad {
59 |
60 | [super viewDidLoad];
61 |
62 | self.locationManager = [[CLLocationManager alloc] init];
63 | self.locationManager.delegate = self;
64 |
65 | self.userHeight = 1.6;
66 | self.userLocation = self.locationManager.location;
67 |
68 | self.northButton.enabled = self.arView.isMagenticNorthAvailable;
69 |
70 | // A single image shape in the X/Y plane at the user's location
71 | //
72 | PlaceOfInterest *userLocationPOI = [[PlaceOfInterest alloc] init];
73 |
74 | userLocationPOI.title = NSLocalizedString(@"Standort", nil);
75 | userLocationPOI.targetPosition = GLKVector3Make(0, 0, -2.0 * self.userHeight);
76 |
77 | TGLARImageShape *shape = [[TGLARImageShape alloc] initWithContext:self.arView.renderContext size:CGSizeMake(2, 2) image:[UIImage imageNamed:@"Compass"]];
78 |
79 | shape.transform = GLKMatrix4Multiply(GLKMatrix4MakeRotation(M_PI, 0, 0, 1), GLKMatrix4MakeRotation(-M_PI_2, 0, 1, 0));
80 |
81 | userLocationPOI.overlayShape = shape;
82 |
83 | self.userLocationPOI = userLocationPOI;
84 | }
85 |
86 | - (void)viewWillAppear:(BOOL)animated {
87 |
88 | [super viewWillAppear:animated];
89 |
90 | CLAuthorizationStatus status = [CLLocationManager authorizationStatus];
91 |
92 | [self updateLocationServiceAuthorizationStatus:status];
93 |
94 | [self createOverlaysForPlaces:self.places];
95 | [self.arView reloadData];
96 |
97 | [self.arView start];
98 |
99 | self.arView.interfaceOrientation = [UIApplication sharedApplication].statusBarOrientation;
100 | }
101 |
102 | - (void)viewDidDisappear:(BOOL)animated {
103 |
104 | [super viewDidDisappear:animated];
105 |
106 | [self.arView stop];
107 |
108 | [self.locationManager stopUpdatingLocation];
109 | }
110 |
111 | #pragma mark - Appearance
112 |
113 | - (UIInterfaceOrientationMask)supportedInterfaceOrientations {
114 |
115 | return UIInterfaceOrientationMaskAll;
116 | }
117 |
118 | - (UIStatusBarStyle)preferredStatusBarStyle {
119 |
120 | return UIStatusBarStyleLightContent;
121 | }
122 |
123 | - (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id)coordinator {
124 |
125 | [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
126 |
127 | [coordinator animateAlongsideTransition:nil completion:^(id coordinator) {
128 |
129 | if ([UIApplication sharedApplication].statusBarOrientation != self.arView.interfaceOrientation) {
130 |
131 | self.arView.interfaceOrientation = [UIApplication sharedApplication].statusBarOrientation;
132 | }
133 | }];
134 | }
135 |
136 | - (IBAction)closeSearch:(UIStoryboardSegue *)segue {
137 |
138 | SearchViewController *controller = segue.sourceViewController;
139 |
140 | self.places = controller.foundPOIs;
141 | }
142 |
143 | #pragma mark - Accessors
144 |
145 | - (void)setUserHeight:(float)userHeight {
146 |
147 | _userHeight = userHeight;
148 |
149 | self.userLocationPOI.overlayShape.transform = GLKMatrix4MakeTranslation(0, 0, -self.userHeight);
150 | }
151 |
152 | - (void)setUserLocation:(CLLocation *)userLocation {
153 |
154 | if (userLocation) {
155 |
156 | _userLocation = userLocation;
157 |
158 | [self updatePlaceOverlayPositions];
159 | }
160 | }
161 |
162 | - (void)setPlaces:(NSArray *)places {
163 |
164 | if (self.isViewLoaded) {
165 |
166 | [self destroyOverlaysForPlaces:self.places];
167 | }
168 |
169 | _places = places;
170 |
171 | if (self.isViewLoaded) {
172 |
173 | [self createOverlaysForPlaces:self.places];
174 | [self.arView reloadData];
175 | }
176 | }
177 |
178 | #pragma mark - Navigation
179 |
180 | - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
181 |
182 | if ([segue.identifier isEqualToString:@"OpenSearch"]) {
183 |
184 | UINavigationController *navigation = segue.destinationViewController;
185 | SearchViewController *controller = (SearchViewController *)navigation.topViewController;
186 |
187 | controller.currentLocation = self.userLocation;
188 | controller.foundPOIs = self.places;
189 | }
190 | }
191 |
192 | #pragma mark - Actions
193 |
194 | - (IBAction)toggleTrueNorth:(id)sender {
195 |
196 | self.arView.useTrueNorth = !self.arView.isUsingTrueNorth;
197 | self.northButton.selected = self.arView.isUsingTrueNorth;
198 | }
199 |
200 | #pragma mark - CLLocationManagerDelegate protocol
201 |
202 | - (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status {
203 |
204 | [self updateLocationServiceAuthorizationStatus:status];
205 | }
206 |
207 | - (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations {
208 |
209 | self.userLocation = locations.lastObject;
210 | }
211 |
212 | - (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error {
213 |
214 | if (error.code != kCLErrorLocationUnknown) {
215 |
216 | UIAlertController *alert = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"Standort kann nicht ermittelt werden", nil)
217 | message:error.localizedDescription
218 | preferredStyle:UIAlertControllerStyleAlert];
219 |
220 | UIAlertAction *ok = [UIAlertAction actionWithTitle:NSLocalizedString(@"OK", nil)
221 | style:UIAlertActionStyleCancel
222 | handler:nil];
223 |
224 | [alert addAction:ok];
225 |
226 | [self presentViewController:alert animated:YES completion:nil];
227 | }
228 | }
229 |
230 | - (void)updateLocationServiceAuthorizationStatus:(CLAuthorizationStatus)status {
231 |
232 | if (status == kCLAuthorizationStatusRestricted || status == kCLAuthorizationStatusDenied) {
233 |
234 | UIAlertController *alert = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"Kein Zugriff auf Standort", nil)
235 | message:NSLocalizedString(@"Dein Standort kann nicht angezeigt werden.\n\nGehe zu den Einstellungen, um den Zugriff zu erlauben.", nil)
236 | preferredStyle:UIAlertControllerStyleAlert];
237 |
238 | UIAlertAction *ok = [UIAlertAction actionWithTitle:NSLocalizedString(@"OK", nil)
239 | style:UIAlertActionStyleCancel
240 | handler:nil];
241 |
242 | [alert addAction:ok];
243 |
244 | UIAlertAction *settings = [UIAlertAction actionWithTitle:NSLocalizedString(@"Einstellungen", nil)
245 | style:UIAlertActionStyleDefault
246 | handler:^ (UIAlertAction *action) {
247 |
248 | NSURL *url = [NSURL URLWithString:UIApplicationOpenSettingsURLString];
249 |
250 | [[UIApplication sharedApplication] openURL:url];
251 | }];
252 |
253 | [alert addAction:settings];
254 |
255 | [self presentViewController:alert animated:YES completion:nil];
256 |
257 | } else if (status == kCLAuthorizationStatusNotDetermined) {
258 |
259 | [self.locationManager requestWhenInUseAuthorization];
260 |
261 | } else if (status == kCLAuthorizationStatusAuthorizedWhenInUse) {
262 |
263 | [self.locationManager startUpdatingLocation];
264 | }
265 | }
266 |
267 | #pragma mark - TGLARViewDataSource protocol
268 |
269 | - (NSInteger)numberOfOverlaysInARView:(TGLARView *)arview {
270 |
271 | // All the POIs plus the user location overlay
272 | return self.places.count + 1;
273 | }
274 |
275 | - (id)arView:(TGLARView *)arview overlayAtIndex:(NSInteger)index {
276 |
277 | return (index < self.places.count) ? self.places[index] : self.userLocationPOI;
278 | }
279 |
280 | #pragma mark - TGLARViewDelegate protocol
281 |
282 | - (void)arView:(TGLARView *)arview didTapViewOverlay:(TGLARViewOverlay *)view {
283 |
284 | PlaceOfInterest *poi = (PlaceOfInterest *)view.overlay;
285 |
286 | UIAlertController *alert = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"Callout angetippt", nil)
287 | message:poi.title
288 | preferredStyle:UIAlertControllerStyleAlert];
289 |
290 | UIAlertAction *ok = [UIAlertAction actionWithTitle:NSLocalizedString(@"OK", nil)
291 | style:UIAlertActionStyleCancel
292 | handler:nil];
293 |
294 | [alert addAction:ok];
295 |
296 | [self presentViewController:alert animated:YES completion:nil];
297 | }
298 |
299 | - (void)arView:(TGLARView *)arview didTapShapeOverlay:(TGLARShapeOverlay *)shape {
300 |
301 | PlaceOfInterest *poi = (PlaceOfInterest *)shape.overlay;
302 |
303 | if (poi != self.userLocationPOI) {
304 |
305 | UIAlertController *alert = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"Shape angetippt", nil)
306 | message:poi.title
307 | preferredStyle:UIAlertControllerStyleAlert];
308 |
309 | UIAlertAction *ok = [UIAlertAction actionWithTitle:NSLocalizedString(@"OK", nil)
310 | style:UIAlertActionStyleCancel
311 | handler:nil];
312 |
313 | [alert addAction:ok];
314 |
315 | [self presentViewController:alert animated:YES completion:nil];
316 | }
317 | }
318 |
319 | - (CGFloat)arViewShapeOverlayNearClippingDistance:(TGLARView *)arview {
320 |
321 | return 1.0;
322 | }
323 |
324 | - (CGFloat)arViewShapeOverlayFarClippingDistance:(TGLARView *)arview {
325 |
326 | return 2000.0;
327 | }
328 |
329 | #pragma amrk - PlaceOfInterestViewDelegate protocol
330 |
331 | - (void)poiViewButtonTapped:(PlaceOfInterestView *)view {
332 |
333 | PlaceOfInterest *poi = (PlaceOfInterest *)view.overlay;
334 |
335 | UIAlertController *alert = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"Info angetippt", nil)
336 | message:poi.title
337 | preferredStyle:UIAlertControllerStyleAlert];
338 |
339 | UIAlertAction *ok = [UIAlertAction actionWithTitle:NSLocalizedString(@"OK", nil)
340 | style:UIAlertActionStyleCancel
341 | handler:nil];
342 |
343 | [alert addAction:ok];
344 |
345 | [self presentViewController:alert animated:YES completion:nil];
346 | }
347 |
348 | #pragma mark - Helpers
349 |
350 | - (void)createOverlaysForPlaces:(NSArray *)places {
351 |
352 | for (PlaceOfInterest *place in places) {
353 |
354 | if (!place.overlayView) {
355 |
356 | PlaceOfInterestView *overlayView = [[PlaceOfInterestView alloc] init];
357 |
358 | overlayView.place = place;
359 | overlayView.delegate = self;
360 | overlayView.calloutLineColor = [UIColor redColor];
361 | overlayView.contentView.backgroundColor = [UIColor redColor];
362 |
363 | place.overlayView = overlayView;
364 | }
365 |
366 | if (!place.overlayShape) {
367 |
368 | place.overlayShape = [[TGLARBillboardImageShape alloc] initWithContext:self.arView.renderContext size:CGSizeMake(100.0, 100.0) image:[UIImage imageNamed:@"Target"]];
369 | }
370 | }
371 |
372 | [self updatePlaceOverlayPositions];
373 | }
374 |
375 | - (void)updatePlaceOverlayPositions {
376 |
377 | // The overlay -targetPositions are relative to the
378 | // camera position, which is located at the origin.
379 | //
380 | // Therefore we compute the distances in the X and Y
381 | // directions and set the overlay position accordingly.
382 | //
383 | // NOTE: Since the POI altitued are always zero we
384 | // do not set the Z component here.
385 | //
386 | MKMapPoint userPoint = MKMapPointForCoordinate(self.userLocation.coordinate);
387 |
388 | for (PlaceOfInterest *place in self.places) {
389 |
390 | MKMapPoint overlayPoint = MKMapPointForCoordinate(place.coordinate);
391 |
392 | MKMapPoint westPoint = MKMapPointMake(overlayPoint.x, userPoint.y);
393 | CLLocationDistance westDistance = MKMetersBetweenMapPoints(userPoint, westPoint);
394 |
395 | if (userPoint.x < overlayPoint.x) westDistance = -westDistance;
396 |
397 | MKMapPoint northPoint = MKMapPointMake(userPoint.x, overlayPoint.y);
398 | CLLocationDistance northDistance = MKMetersBetweenMapPoints(userPoint, northPoint);
399 |
400 | if (userPoint.y < overlayPoint.y) northDistance = -northDistance;
401 |
402 | GLKVector3 overlayPosition = GLKVector3Make(northDistance, westDistance, (place == self.userLocationPOI) ? -2.0 * self.userHeight : 0.0);
403 |
404 | place.targetPosition = overlayPosition;
405 | }
406 | }
407 |
408 | - (void)destroyOverlaysForPlaces:(NSArray *)places {
409 |
410 | for (PlaceOfInterest *place in places) {
411 |
412 | place.overlayShape = nil;
413 | place.overlayView = nil;
414 | }
415 | }
416 |
417 | @end
418 |
--------------------------------------------------------------------------------
/TGLAugmentedRealityExample/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/TGLAugmentedRealityExample/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
--------------------------------------------------------------------------------
/TGLAugmentedRealityExample/Compass.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powermobileweb/TGLAugmentedRealityView/2360fc88458f2d07dd9d7412c0e5e25dcf45d9ce/TGLAugmentedRealityExample/Compass.png
--------------------------------------------------------------------------------
/TGLAugmentedRealityExample/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | de_DE
7 | CFBundleDisplayName
8 | TGLAugmented
9 | CFBundleExecutable
10 | $(EXECUTABLE_NAME)
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | APPL
19 | CFBundleShortVersionString
20 | 1.0
21 | CFBundleSignature
22 | ????
23 | CFBundleVersion
24 | 1.0.4
25 | LSRequiresIPhoneOS
26 |
27 | NSCameraUsageDescription
28 | CAMERA_USAGE
29 | NSLocationWhenInUseUsageDescription
30 | LOCATION_WHENINUSE_USAGE
31 | UILaunchStoryboardName
32 | LaunchScreen
33 | UIMainStoryboardFile
34 | Main
35 | UIRequiredDeviceCapabilities
36 |
37 | armv7
38 | gyroscope
39 | location-services
40 | opengles-2
41 | video-camera
42 |
43 | UIRequiresFullScreen
44 |
45 | UIStatusBarStyle
46 | UIStatusBarStyleLightContent
47 | UISupportedInterfaceOrientations
48 |
49 | UIInterfaceOrientationPortrait
50 | UIInterfaceOrientationLandscapeLeft
51 | UIInterfaceOrientationLandscapeRight
52 | UIInterfaceOrientationPortraitUpsideDown
53 |
54 | UISupportedInterfaceOrientations~ipad
55 |
56 | UIInterfaceOrientationPortrait
57 | UIInterfaceOrientationPortraitUpsideDown
58 | UIInterfaceOrientationLandscapeLeft
59 | UIInterfaceOrientationLandscapeRight
60 |
61 |
62 |
63 |
--------------------------------------------------------------------------------
/TGLAugmentedRealityExample/PlaceOfInterest.h:
--------------------------------------------------------------------------------
1 | //
2 | // PlaceOfInterest.h
3 | // TGLAugmentedRealityExample
4 | //
5 | // Created by Tim Gleue on 21.10.13.
6 | // Copyright (c) 2015 PowerMobile Team (https://powermobile.org)
7 | //
8 | // Permission is hereby granted, free of charge, to any person obtaining a copy
9 | // of this software and associated documentation files (the "Software"), to deal
10 | // in the Software without restriction, including without limitation the rights
11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 | // copies of the Software, and to permit persons to whom the Software is
13 | // furnished to do so, subject to the following conditions:
14 | //
15 | // The above copyright notice and this permission notice shall be included in
16 | // all copies or substantial portions of the Software.
17 | //
18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 | // THE SOFTWARE.
25 |
26 | #import
27 | #import
28 | #import
29 |
30 | #import "TGLAROverlay.h"
31 | #import "TGLARViewOverlay.h"
32 | #import "TGLARShapeOverlay.h"
33 |
34 | @interface PlaceOfInterest : NSObject
35 |
36 | @property (nonatomic, copy) NSString *title;
37 | @property (nonatomic, readonly) CLPlacemark *placemark;
38 |
39 | @property (nonatomic, assign) GLKVector3 targetPosition;
40 |
41 | @property (nonatomic, strong) TGLARViewOverlay *overlayView;
42 | @property (nonatomic, strong) TGLARShapeOverlay *overlayShape;
43 |
44 | + (instancetype)placeOfInterestWithPlacemark:(CLPlacemark *)placemark;
45 |
46 | - (instancetype)initWithPlacemark:(CLPlacemark *)placemark;
47 |
48 | @end
49 |
--------------------------------------------------------------------------------
/TGLAugmentedRealityExample/PlaceOfInterest.m:
--------------------------------------------------------------------------------
1 | //
2 | // PlaceOfInterest.m
3 | // TGLAugmentedRealityExample
4 | //
5 | // Created by Tim Gleue on 21.10.13.
6 | // Copyright (c) 2015 PowerMobile Team (https://powermobile.org)
7 | //
8 | // Permission is hereby granted, free of charge, to any person obtaining a copy
9 | // of this software and associated documentation files (the "Software"), to deal
10 | // in the Software without restriction, including without limitation the rights
11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 | // copies of the Software, and to permit persons to whom the Software is
13 | // furnished to do so, subject to the following conditions:
14 | //
15 | // The above copyright notice and this permission notice shall be included in
16 | // all copies or substantial portions of the Software.
17 | //
18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 | // THE SOFTWARE.
25 |
26 | #import "PlaceOfInterest.h"
27 |
28 | @implementation PlaceOfInterest
29 |
30 | + (instancetype)placeOfInterestWithPlacemark:(CLPlacemark *)placemark {
31 |
32 | return [[PlaceOfInterest alloc] initWithPlacemark:placemark];
33 | }
34 |
35 | - (instancetype)initWithPlacemark:(CLPlacemark *)placemark {
36 |
37 | self = [super init];
38 |
39 | if (self) {
40 |
41 | _placemark = placemark;
42 | }
43 |
44 | return self;
45 | }
46 |
47 | #pragma mark - MKAnnotation protocol
48 |
49 | - (CLLocationCoordinate2D)coordinate {
50 |
51 | return self.placemark.location.coordinate;
52 | }
53 |
54 | #pragma mark - TGLAROverlay protocol
55 |
56 | - (void)setOverlayView:(nullable TGLARViewOverlay *)overlayView {
57 |
58 | _overlayView = overlayView;
59 |
60 | self.overlayView.overlay = self;
61 | }
62 |
63 | - (void)setOverlayShape:(nullable TGLARShapeOverlay *)overlayShape {
64 |
65 | _overlayShape = overlayShape;
66 |
67 | self.overlayShape.overlay = self;
68 | }
69 |
70 | @end
71 |
--------------------------------------------------------------------------------
/TGLAugmentedRealityExample/PlaceOfInterestView.h:
--------------------------------------------------------------------------------
1 | //
2 | // PlaceOfInterestView.h
3 | // TGLAugmentedRealityExample
4 | //
5 | // Created by Tim Gleue on 20.11.15.
6 | // Copyright (c) 2015 PowerMobile Team (https://powermobile.org)
7 | //
8 | // Permission is hereby granted, free of charge, to any person obtaining a copy
9 | // of this software and associated documentation files (the "Software"), to deal
10 | // in the Software without restriction, including without limitation the rights
11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 | // copies of the Software, and to permit persons to whom the Software is
13 | // furnished to do so, subject to the following conditions:
14 | //
15 | // The above copyright notice and this permission notice shall be included in
16 | // all copies or substantial portions of the Software.
17 | //
18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 | // THE SOFTWARE.
25 |
26 | #import "TGLARViewOverlay.h"
27 |
28 | #import "PlaceOfInterest.h"
29 |
30 | @class PlaceOfInterestView;
31 |
32 | @protocol PlaceOfInterestViewDelegate
33 |
34 | - (void)poiViewButtonTapped:(PlaceOfInterestView *)view;
35 |
36 | @end
37 |
38 | @interface PlaceOfInterestView : TGLARViewOverlay
39 |
40 | @property (nonatomic, weak) id delegate;
41 |
42 | @property (nonatomic, strong) PlaceOfInterest *place;
43 |
44 | @end
45 |
--------------------------------------------------------------------------------
/TGLAugmentedRealityExample/PlaceOfInterestView.m:
--------------------------------------------------------------------------------
1 | //
2 | // PlaceOfInterestView.m
3 | // TGLAugmentedRealityExample
4 | //
5 | // Created by Tim Gleue on 20.11.15.
6 | // Copyright (c) 2015 PowerMobile Team (https://powermobile.org)
7 | //
8 | // Permission is hereby granted, free of charge, to any person obtaining a copy
9 | // of this software and associated documentation files (the "Software"), to deal
10 | // in the Software without restriction, including without limitation the rights
11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 | // copies of the Software, and to permit persons to whom the Software is
13 | // furnished to do so, subject to the following conditions:
14 | //
15 | // The above copyright notice and this permission notice shall be included in
16 | // all copies or substantial portions of the Software.
17 | //
18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 | // THE SOFTWARE.
25 |
26 | #import "PlaceOfInterestView.h"
27 |
28 | @interface PlaceOfInterestView ()
29 |
30 | @property (nonatomic, weak) UILabel *overlayLabel;
31 | @property (nonatomic, weak) UIButton *overlayButton;
32 |
33 | @end
34 |
35 | @implementation PlaceOfInterestView
36 |
37 | - (instancetype)initWithCoder:(NSCoder *)aDecoder {
38 |
39 | self = [super initWithCoder:aDecoder];
40 |
41 | if (self) [self initView];
42 |
43 | return self;
44 | }
45 |
46 | - (instancetype)initWithFrame:(CGRect)frame {
47 |
48 | self = [super initWithFrame:frame];
49 |
50 | if (self) [self initView];
51 |
52 | return self;
53 | }
54 |
55 | - (void)initView {
56 |
57 | UILabel *overlayLabel = [[UILabel alloc] init];
58 |
59 | overlayLabel.textColor = [UIColor whiteColor];
60 | overlayLabel.numberOfLines = 0;
61 | overlayLabel.translatesAutoresizingMaskIntoConstraints = NO;
62 |
63 | [self.contentView addSubview:overlayLabel];
64 |
65 | UIButton *overlayButton = [UIButton buttonWithType:UIButtonTypeInfoLight];
66 |
67 | overlayButton.tintColor = overlayLabel.textColor;
68 | overlayButton.translatesAutoresizingMaskIntoConstraints = NO;
69 |
70 | [overlayButton addTarget:self action:@selector(buttonTapped:) forControlEvents:UIControlEventTouchUpInside];
71 |
72 | [self.contentView addSubview:overlayButton];
73 |
74 | [self.contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[overlayButton]-[overlayLabel]-|"
75 | options:0
76 | metrics:nil
77 | views:NSDictionaryOfVariableBindings(overlayLabel, overlayButton)]];
78 |
79 | [self.contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-[overlayLabel]-|"
80 | options:0
81 | metrics:nil
82 | views:NSDictionaryOfVariableBindings(overlayLabel, overlayButton)]];
83 |
84 | [self.contentView addConstraint:[NSLayoutConstraint constraintWithItem:overlayButton
85 | attribute:NSLayoutAttributeCenterY
86 | relatedBy:NSLayoutRelationEqual
87 | toItem:self.contentView
88 | attribute:NSLayoutAttributeCenterY
89 | multiplier:1.0
90 | constant:0]];
91 | self.overlayLabel = overlayLabel;
92 | self.overlayButton = overlayButton;
93 | }
94 |
95 | #pragma mark - Accessors
96 |
97 | - (void)setPlace:(PlaceOfInterest *)place {
98 |
99 | _place = place;
100 |
101 | self.overlayLabel.text = place.title;
102 | }
103 |
104 | #pragma mark - Actions
105 |
106 | - (IBAction)buttonTapped:(id)sender {
107 |
108 | [self.delegate poiViewButtonTapped:self];
109 | }
110 |
111 | @end
112 |
--------------------------------------------------------------------------------
/TGLAugmentedRealityExample/SearchViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // SearchViewController.h
3 | // TGLAugmentedRealityExample
4 | //
5 | // Created by Tim Gleue on 06.11.15.
6 | // Copyright (c) 2015 PowerMobile Team (https://powermobile.org)
7 | //
8 | // Permission is hereby granted, free of charge, to any person obtaining a copy
9 | // of this software and associated documentation files (the "Software"), to deal
10 | // in the Software without restriction, including without limitation the rights
11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 | // copies of the Software, and to permit persons to whom the Software is
13 | // furnished to do so, subject to the following conditions:
14 | //
15 | // The above copyright notice and this permission notice shall be included in
16 | // all copies or substantial portions of the Software.
17 | //
18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 | // THE SOFTWARE.
25 |
26 | #import
27 |
28 | #import "PlaceOfInterest.h"
29 |
30 | @interface SearchViewController : UIViewController
31 |
32 | @property (nonatomic, strong) CLLocation *currentLocation;
33 |
34 | @property (nonatomic, strong) NSArray *foundPOIs;
35 |
36 | @end
37 |
38 |
--------------------------------------------------------------------------------
/TGLAugmentedRealityExample/SearchViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // SearchViewController.m
3 | // TGLAugmentedRealityExample
4 | //
5 | // Created by Tim Gleue on 06.11.15.
6 | // Copyright (c) 2015 PowerMobile Team (https://powermobile.org)
7 | //
8 | // Permission is hereby granted, free of charge, to any person obtaining a copy
9 | // of this software and associated documentation files (the "Software"), to deal
10 | // in the Software without restriction, including without limitation the rights
11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 | // copies of the Software, and to permit persons to whom the Software is
13 | // furnished to do so, subject to the following conditions:
14 | //
15 | // The above copyright notice and this permission notice shall be included in
16 | // all copies or substantial portions of the Software.
17 | //
18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 | // THE SOFTWARE.
25 |
26 | #import "SearchViewController.h"
27 |
28 | #import
29 |
30 | static inline UIViewAnimationOptions animationOptionsWithCurve(UIViewAnimationCurve curve) {
31 |
32 | // See: http://stackoverflow.com/a/20188994
33 | //
34 | UIViewAnimationOptions opt = (UIViewAnimationOptions)curve;
35 | return opt << 16;
36 | }
37 |
38 | @interface SearchViewController ()
39 |
40 | @property (weak, nonatomic) IBOutlet MKMapView *mapView;
41 | @property (weak, nonatomic) IBOutlet UISearchBar *searchBar;
42 | @property (weak, nonatomic) IBOutlet NSLayoutConstraint *searchBarBottomLayoutConstraint;
43 |
44 | @property (nonatomic, readonly) MKDistanceFormatter *distanceFormatter;
45 |
46 | @end
47 |
48 | @implementation SearchViewController
49 |
50 | @synthesize distanceFormatter = _distanceFormatter;
51 |
52 | - (void)dealloc {
53 |
54 | [[NSNotificationCenter defaultCenter] removeObserver:self];
55 | }
56 |
57 | - (void)viewDidLoad {
58 |
59 | [super viewDidLoad];
60 |
61 | self.mapView.showsUserLocation = YES;
62 | self.mapView.userTrackingMode = MKUserTrackingModeNone;
63 |
64 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
65 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
66 |
67 | [self.searchBar becomeFirstResponder];
68 | }
69 |
70 | - (void)viewWillAppear:(BOOL)animated {
71 |
72 | [super viewWillAppear:animated];
73 |
74 | [self.mapView addAnnotations:self.foundPOIs];
75 | }
76 |
77 | #pragma mark - Accessors
78 |
79 | - (void)setCurrentLocation:(CLLocation *)currentLocation {
80 |
81 | _currentLocation = currentLocation;
82 |
83 | MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance(currentLocation.coordinate, 2000.0, 2000.0);
84 |
85 | [self.mapView setRegion:region animated:YES];
86 | }
87 |
88 | - (MKDistanceFormatter *)distanceFormatter {
89 |
90 | if (_distanceFormatter == nil) {
91 |
92 | _distanceFormatter = [[MKDistanceFormatter alloc] init];
93 | }
94 |
95 | return _distanceFormatter;
96 | }
97 |
98 | #pragma mark - MKMapViewDelegate protocol
99 |
100 | - (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id)annotation {
101 |
102 | if (![annotation isKindOfClass:MKUserLocation.class]) {
103 |
104 | static NSString * const ident = @"MapAnnotation";
105 |
106 | MKAnnotationView *view = [mapView dequeueReusableAnnotationViewWithIdentifier:ident];
107 |
108 | if (!view) {
109 |
110 | view = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:ident];
111 |
112 | view.image = [UIImage imageNamed:@"Pin"];
113 | view.centerOffset = CGPointMake(0.0, -0.5 * view.image.size.height + 8.0);
114 | view.canShowCallout = YES;
115 | view.calloutOffset = CGPointMake(0.0, -0.1 * view.image.size.height);
116 |
117 | view.leftCalloutAccessoryView = [[UIImageView alloc] initWithImage:view.image];
118 |
119 | UIButton *button = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
120 |
121 | view.rightCalloutAccessoryView = button;
122 |
123 | UILabel *detailLabel = [[UILabel alloc] init];
124 |
125 | detailLabel.font = [UIFont systemFontOfSize:10.0];
126 | detailLabel.textColor = [UIColor lightGrayColor];
127 |
128 | view.detailCalloutAccessoryView = detailLabel;
129 |
130 | } else {
131 |
132 | view.annotation = annotation;
133 | }
134 |
135 | if ([annotation isKindOfClass:PlaceOfInterest.class]) {
136 |
137 | UILabel *detailLabel = (UILabel *)view.detailCalloutAccessoryView;
138 | PlaceOfInterest *poi = (PlaceOfInterest *)annotation;
139 |
140 | CLLocationDistance dist = [poi.placemark.location distanceFromLocation:self.currentLocation];
141 |
142 | detailLabel.text = [self.distanceFormatter stringFromDistance:dist];
143 | }
144 |
145 | return view;
146 | }
147 |
148 | return nil;
149 | }
150 |
151 | - (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control {
152 |
153 | for (PlaceOfInterest *poi in self.foundPOIs) {
154 |
155 | if (poi == view.annotation) {
156 |
157 | [mapView removeAnnotation:poi];
158 |
159 | NSMutableArray *pois = [NSMutableArray arrayWithArray:self.foundPOIs];
160 |
161 | [pois removeObject:poi];
162 | self.foundPOIs = pois;
163 |
164 | break;
165 | }
166 | }
167 | }
168 |
169 | #pragma mark - UISearchBarDelegate protocol
170 |
171 | - (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar {
172 |
173 | [searchBar resignFirstResponder];
174 |
175 | MKLocalSearchRequest *request = [[MKLocalSearchRequest alloc] init];
176 |
177 | request.naturalLanguageQuery = searchBar.text;
178 | request.region = self.mapView.region;
179 |
180 | MKLocalSearch *search = [[MKLocalSearch alloc] initWithRequest:request];
181 |
182 | [search startWithCompletionHandler:^(MKLocalSearchResponse *response, NSError *error) {
183 |
184 | // Create a POI for each found item
185 | //
186 | NSMutableArray *places = [NSMutableArray array];
187 |
188 | for (MKMapItem *item in response.mapItems) {
189 |
190 | PlaceOfInterest *poi = [PlaceOfInterest placeOfInterestWithPlacemark:item.placemark];
191 |
192 | CLLocationDistance dist = [item.placemark.location distanceFromLocation:self.currentLocation];
193 |
194 | poi.title = [NSString stringWithFormat:@"%@ (%@)", item.name, [self.distanceFormatter stringFromDistance:dist]];
195 |
196 | [places addObject:poi];
197 | }
198 |
199 | [self.mapView removeAnnotations:[self.mapView annotations]];
200 | [self.mapView showAnnotations:places animated:YES];
201 |
202 | self.foundPOIs = places;
203 | }];
204 | }
205 |
206 | - (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar {
207 |
208 | [searchBar resignFirstResponder];
209 | }
210 |
211 | #pragma mark - Notification handlers
212 |
213 | - (void)keyboardWillShow:(NSNotification *)notification {
214 |
215 | [self.searchBar setShowsCancelButton:YES animated:YES];
216 |
217 | NSValue *value = notification.userInfo[UIKeyboardFrameEndUserInfoKey];
218 | CGRect frame = [value CGRectValue];
219 |
220 | self.searchBarBottomLayoutConstraint.constant = frame.size.height;
221 |
222 | if (@available(iOS 11, *)) self.searchBarBottomLayoutConstraint.constant -= self.view.safeAreaInsets.bottom;
223 |
224 | NSTimeInterval duration = [notification.userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue];
225 | UIViewAnimationCurve curve = [notification.userInfo[UIKeyboardAnimationCurveUserInfoKey] integerValue];
226 |
227 | [UIView animateWithDuration:duration
228 | delay:0
229 | options:animationOptionsWithCurve(curve)
230 | animations:^ (void) {
231 |
232 | [self.view layoutIfNeeded];
233 |
234 | } completion:nil];
235 | }
236 |
237 | - (void)keyboardWillHide:(NSNotification *)notification {
238 |
239 | [self.searchBar setShowsCancelButton:NO animated:YES];
240 |
241 | self.searchBarBottomLayoutConstraint.constant = 0;
242 |
243 | NSTimeInterval duration = [notification.userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue];
244 | UIViewAnimationCurve curve = [notification.userInfo[UIKeyboardAnimationCurveUserInfoKey] integerValue];
245 |
246 | [UIView animateWithDuration:duration
247 | delay:0
248 | options:animationOptionsWithCurve(curve)
249 | animations:^ (void) {
250 |
251 | [self.view layoutIfNeeded];
252 |
253 | } completion:nil];
254 | }
255 |
256 | @end
257 |
--------------------------------------------------------------------------------
/TGLAugmentedRealityExample/Target.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/powermobileweb/TGLAugmentedRealityView/2360fc88458f2d07dd9d7412c0e5e25dcf45d9ce/TGLAugmentedRealityExample/Target.png
--------------------------------------------------------------------------------
/TGLAugmentedRealityExample/de.lproj/InfoPlist.strings:
--------------------------------------------------------------------------------
1 | /*
2 | InfoPlist.strings
3 | TGLAugmentedRealityExample
4 |
5 | Created by Tim Gleue on 26.11.15.
6 | Copyright © 2015 Tim Gleue. All rights reserved.
7 | */
8 |
9 | "NSLocationWhenInUseUsageDescription" = "Dein Standort wird verwendet, um POIs in Deiner Nähe zu suchen und anzuzeigen.";
10 | "NSCameraUsageDescription" = "Die Kamera wird verwendet, um POIs in Deiner Nähe anzuzeigen.";
11 |
--------------------------------------------------------------------------------
/TGLAugmentedRealityExample/de.lproj/Localizable.strings:
--------------------------------------------------------------------------------
1 | /*
2 | Localizable.strings
3 | TGLAugmentedRealityExample
4 |
5 | Created by Tim Gleue on 26.11.15.
6 | Copyright © 2015 Tim Gleue. All rights reserved.
7 | */
8 |
9 | "OK" = "OK";
10 |
11 | "Standort" = "Standort";
12 | "Einstellungen" = "Einstellungen";
13 |
14 | "Standort kann nicht ermittelt werden" = "Standort kann nicht ermittelt werden";
15 | "Kein Zugriff auf Standort" = "Kein Zugriff auf Standort";
16 | "Dein Standort kann nicht angezeigt werden.\n\nGehe zu den Einstellungen, um den Zugriff zu erlauben." = "Dein Standort kann nicht angezeigt werden.\n\nGehe zu den Einstellungen, um den Zugriff zu erlauben.";
17 |
18 | "N" = "N";
19 | "NE" = "NE";
20 | "E" = "E";
21 | "SE" = "SE";
22 | "S" = "S";
23 | "SW" = "SW";
24 | "W" = "W";
25 | "NW" = "NW";
26 |
27 | "Callout angetippt" = "Callout angetippt";
28 | "Shape angetippt" = "Shape angetippt";
29 | "Info angetippt" = "Info angetippt";
30 |
--------------------------------------------------------------------------------
/TGLAugmentedRealityExample/en.lproj/InfoPlist.strings:
--------------------------------------------------------------------------------
1 | /*
2 | InfoPlist.strings
3 | TGLAugmentedRealityExample
4 |
5 | Created by Tim Gleue on 26.11.15.
6 | Copyright © 2015 Tim Gleue. All rights reserved.
7 | */
8 |
9 | "NSLocationWhenInUseUsageDescription" = "Location will be used to find and display nearby POIs.";
10 | "NSCameraUsageDescription" = "Camera will be used to display nearby POIs.";
11 |
--------------------------------------------------------------------------------
/TGLAugmentedRealityExample/en.lproj/Localizable.strings:
--------------------------------------------------------------------------------
1 | /*
2 | Localizable.strings
3 | TGLAugmentedRealityExample
4 |
5 | Created by Tim Gleue on 26.11.15.
6 | Copyright © 2015 Tim Gleue. All rights reserved.
7 | */
8 |
9 | "OK" = "OK";
10 |
11 | "Standort" = "Location";
12 | "Einstellungen" = "Settings";
13 |
14 | "Standort kann nicht ermittelt werden" = "Location Could Not Be Determined";
15 | "Kein Zugriff auf Standort" = "Cannot Access Location";
16 | "Dein Standort kann nicht angezeigt werden.\n\nGehe zu den Einstellungen, um den Zugriff zu erlauben." = "Your location cannot be displayed.\n\nGot to Settings to allow location access.";
17 |
18 | "N" = "N";
19 | "NE" = "NE";
20 | "E" = "E";
21 | "SE" = "SE";
22 | "S" = "S";
23 | "SW" = "SW";
24 | "W" = "W";
25 | "NW" = "NW";
26 |
27 | "Callout angetippt" = "Callout tapped";
28 | "Shape angetippt" = "Shape tapped";
29 | "Info angetippt" = "Info tapped";
30 |
--------------------------------------------------------------------------------
/TGLAugmentedRealityExample/en.lproj/Main.strings:
--------------------------------------------------------------------------------
1 |
2 | /* Class = "UISearchBar"; placeholder = "POI"; ObjectID = "KbV-fU-cvd"; */
3 | "KbV-fU-cvd.placeholder" = "POI";
4 |
5 | /* Class = "UIButton"; disabledTitle = "Kein Kompass"; ObjectID = "Qf4-Qj-78z"; */
6 | "Qf4-Qj-78z.disabledTitle" = "No Compass";
7 |
8 | /* Class = "UIButton"; normalTitle = "Magnetisch N"; ObjectID = "Qf4-Qj-78z"; */
9 | "Qf4-Qj-78z.normalTitle" = "Magnetic N";
10 |
11 | /* Class = "UIButton"; selectedTitle = "Geographisch N"; ObjectID = "Qf4-Qj-78z"; */
12 | "Qf4-Qj-78z.selectedTitle" = "Geographic N";
13 |
14 | /* Class = "UINavigationItem"; title = "POI Suchen"; ObjectID = "dDA-fI-Tqz"; */
15 | "dDA-fI-Tqz.title" = "POI Suchen";
16 |
--------------------------------------------------------------------------------
/TGLAugmentedRealityExample/main.m:
--------------------------------------------------------------------------------
1 |
2 | //
3 | // main.m
4 | // TGLAugmentedRealityExample
5 | //
6 | // Created by Tim Gleue on 06.11.15.
7 | // Copyright (c) 2015 PowerMobile Team (https://powermobile.org)
8 | //
9 | // Permission is hereby granted, free of charge, to any person obtaining a copy
10 | // of this software and associated documentation files (the "Software"), to deal
11 | // in the Software without restriction, including without limitation the rights
12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 | // copies of the Software, and to permit persons to whom the Software is
14 | // furnished to do so, subject to the following conditions:
15 | //
16 | // The above copyright notice and this permission notice shall be included in
17 | // all copies or substantial portions of the Software.
18 | //
19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 | // THE SOFTWARE.
26 |
27 | #import
28 | #import "AppDelegate.h"
29 |
30 | int main(int argc, char * argv[]) {
31 | @autoreleasepool {
32 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/TGLAugmentedRealityView.podspec:
--------------------------------------------------------------------------------
1 | Pod::Spec.new do |s|
2 | s.name = 'TGLAugmentedRealityView'
3 | s.version = '1.0.4'
4 | s.license = 'MIT'
5 | s.summary = 'Place overlays on a camera preview and adjust their position depending on device attitude.'
6 | s.homepage = 'https://github.com/gleue/TGLAugmentedRealityView'
7 | s.authors = { 'Tim Gleue' => 'info@powermobile.org' }
8 | s.source = { :git => 'https://github.com/[powermobileteam]/TGLAugmentedRealityView.git', :tag => s.version.to_s }
9 | s.source_files = 'TGLAugmentedRealityView'
10 |
11 | s.requires_arc = true
12 | s.platform = :ios, '8.0'
13 | end
14 |
--------------------------------------------------------------------------------
/TGLAugmentedRealityView/TGLARBillboardImageShape.h:
--------------------------------------------------------------------------------
1 | //
2 | // TGLARBillboardImageShape.h
3 | // TGLAugmentedRealityView
4 | //
5 | // Created by Tim Gleue on 19.11.15.
6 | // Copyright (c) 2015 PowerMobile Team ( http://powermobile.org )
7 | //
8 | // Permission is hereby granted, free of charge, to any person obtaining a copy
9 | // of this software and associated documentation files (the "Software"), to deal
10 | // in the Software without restriction, including without limitation the rights
11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 | // copies of the Software, and to permit persons to whom the Software is
13 | // furnished to do so, subject to the following conditions:
14 | //
15 | // The above copyright notice and this permission notice shall be included in
16 | // all copies or substantial portions of the Software.
17 | //
18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 | // THE SOFTWARE.
25 |
26 | #import "TGLARImageShape.h"
27 |
28 | /// A @p TGLARImageShape automatically rotating towards the camera around its origin.
29 | @interface TGLARBillboardImageShape : TGLARImageShape
30 |
31 | /** Disables billboard transformation. Default is NO. */
32 | @property (nonatomic, assign, getter=isLocked) BOOL locked;
33 |
34 | @end
35 |
--------------------------------------------------------------------------------
/TGLAugmentedRealityView/TGLARBillboardImageShape.m:
--------------------------------------------------------------------------------
1 | //
2 | // TGLARBillboardImageShape.m
3 | // TGLAugmentedRealityView
4 | //
5 | // Created by Tim Gleue on 19.11.15.
6 | // Copyright (c) 2015 PowerMobile Team (https://powermobile.org)
7 | //
8 | // Permission is hereby granted, free of charge, to any person obtaining a copy
9 | // of this software and associated documentation files (the "Software"), to deal
10 | // in the Software without restriction, including without limitation the rights
11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 | // copies of the Software, and to permit persons to whom the Software is
13 | // furnished to do so, subject to the following conditions:
14 | //
15 | // The above copyright notice and this permission notice shall be included in
16 | // all copies or substantial portions of the Software.
17 | //
18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 | // THE SOFTWARE.
25 |
26 | #import "TGLARBillboardImageShape.h"
27 |
28 | @implementation TGLARBillboardImageShape
29 |
30 | - (BOOL)draw {
31 |
32 | if (self.locked) return [super draw];
33 |
34 | // Compute billboard transform and
35 | // replace shape transform temporarily
36 | // for drawing
37 | //
38 | // see http://nehe.gamedev.net/article/billboarding_how_to/18011/
39 | //
40 | GLKVector3 pos = self.overlay.targetPosition;
41 | GLKVector3 look = GLKVector3Normalize(GLKVector3Negate(pos));
42 | GLKVector3 right = GLKVector3CrossProduct(GLKVector3Make(0, 0, 1), look);
43 | GLKVector3 up = GLKVector3CrossProduct(look, right);
44 |
45 | GLKMatrix4 billboard = GLKMatrix4Make(look.x, look.y, look.z, 0.0, right.x, right.y, right.z, 0.0, up.x, up.y, up.z, 0.0, pos.x, pos.y, pos.z, 1.0);
46 | GLKMatrix4 transform = self.transform;
47 |
48 | self.transform = GLKMatrix4Multiply(billboard, transform);
49 |
50 | BOOL ok = [super draw];
51 |
52 | self.transform = transform;
53 |
54 | return ok;
55 | }
56 |
57 | @end
58 |
--------------------------------------------------------------------------------
/TGLAugmentedRealityView/TGLARCompass.h:
--------------------------------------------------------------------------------
1 | //
2 | // TGLARCompass.h
3 | // TGLAugmentedRealityView
4 | //
5 | // Created by Tim Gleue on 26.11.15.
6 | // Copyright (c) 2015 PowerMobile Team (https://powermobile.org)
7 | //
8 | // Permission is hereby granted, free of charge, to any person obtaining a copy
9 | // of this software and associated documentation files (the "Software"), to deal
10 | // in the Software without restriction, including without limitation the rights
11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 | // copies of the Software, and to permit persons to whom the Software is
13 | // furnished to do so, subject to the following conditions:
14 | //
15 | // The above copyright notice and this permission notice shall be included in
16 | // all copies or substantial portions of the Software.
17 | //
18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 | // THE SOFTWARE.
25 |
26 | #import
27 |
28 | /// An object presenting the current heading angle from a @p TGLARView must adopt this protocol.
29 | @protocol TGLARCompass
30 |
31 | /// The current heading angle in degrees.
32 | - (void)setHeadingAngle:(CGFloat)headingAngle;
33 |
34 | @optional
35 |
36 | /** The current field of view in degrees.
37 | *
38 | * If the receiver responds to this selector,
39 | * the camera's horizontal field of view is set
40 | * here.
41 | */
42 | - (void)setFieldOfView:(CGFloat)fieldOfView;
43 |
44 | @end
45 |
--------------------------------------------------------------------------------
/TGLAugmentedRealityView/TGLARCompassView.h:
--------------------------------------------------------------------------------
1 | //
2 | // TGLARCompassView.h
3 | // TGLAugmentedRealityView
4 | //
5 | // Created by Tim Gleue on 19.11.15.
6 | // Copyright (c) 2015 PowerMobile Team (https://powermobile.org)
7 | //
8 | // Permission is hereby granted, free of charge, to any person obtaining a copy
9 | // of this software and associated documentation files (the "Software"), to deal
10 | // in the Software without restriction, including without limitation the rights
11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 | // copies of the Software, and to permit persons to whom the Software is
13 | // furnished to do so, subject to the following conditions:
14 | //
15 | // The above copyright notice and this permission notice shall be included in
16 | // all copies or substantial portions of the Software.
17 | //
18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 | // THE SOFTWARE.
25 |
26 | #import
27 | #import
28 |
29 | #import "TGLARCompass.h"
30 |
31 | IB_DESIGNABLE
32 |
33 | /** A @p UIView subclass presenting the current heading angle as a horizontal HUD.
34 | *
35 | * The HUD has an upper scale with 16 lines at 22.5 degrees, a lower scale with
36 | * 72 lines at 5 degrees and centered labels for the 8 main directions of the
37 | * compass rose.
38 | */
39 | @interface TGLARCompassView : UIView
40 |
41 | /// The compass label font. Default is bold system font at 24 points.
42 | @property (nonatomic, copy, nullable) IBInspectable UIFont *labelFont;
43 | /// The compass label color. Default is @p [UIColor whiteColor].
44 | @property (nonatomic, copy, nullable) IBInspectable UIColor *labelColor;
45 |
46 | /// The north direction color. Default is @p [UIColor redColor].
47 | @property (nonatomic, copy, nullable) IBInspectable UIColor *northColor;
48 | /// The north scale line width. Default is @p 4.0.
49 | @property (nonatomic, assign) IBInspectable CGFloat northLineWidth;
50 |
51 | /// The upper compass scale color. Default is @p [UIColor whiteColor].
52 | @property (nonatomic, copy, nullable) IBInspectable UIColor *topScaleColor;
53 | /// The upper scale line width. Default is @p 2.0.
54 | @property (nonatomic, assign) IBInspectable CGFloat topScaleLineWidth;
55 |
56 | /// The lower compass scale color. Default is @p [UIColor whiteColor].
57 | @property (nonatomic, copy, nullable) IBInspectable UIColor *bottomScaleColor;
58 | /// The lower scale line width. Default is @p 2.0.
59 | @property (nonatomic, assign) IBInspectable CGFloat bottomScaleLineWidth;
60 |
61 | @end
62 |
--------------------------------------------------------------------------------
/TGLAugmentedRealityView/TGLARCompassView.m:
--------------------------------------------------------------------------------
1 | //
2 | // TGLARCompassView.m
3 | // TGLAugmentedRealityView
4 | //
5 | // Created by Tim Gleue on 19.11.15.
6 | // Copyright (c) 2015 PowerMobile Team (https://powermobile.org)
7 | //
8 | // Permission is hereby granted, free of charge, to any person obtaining a copy
9 | // of this software and associated documentation files (the "Software"), to deal
10 | // in the Software without restriction, including without limitation the rights
11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 | // copies of the Software, and to permit persons to whom the Software is
13 | // furnished to do so, subject to the following conditions:
14 | //
15 | // The above copyright notice and this permission notice shall be included in
16 | // all copies or substantial portions of the Software.
17 | //
18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 | // THE SOFTWARE.
25 |
26 | #import
27 |
28 | #import "TGLARCompassView.h"
29 |
30 | @interface TGLARCompassView ()
31 |
32 | @property (nonatomic, assign) CGFloat fieldOfView;
33 | @property (nonatomic, assign) CGFloat headingAngle;
34 |
35 | @property (nonatomic, readonly) NSDictionary *compassLabels;
36 |
37 | @end
38 |
39 | @implementation TGLARCompassView
40 |
41 | @synthesize compassLabels = _compassLabels;
42 |
43 | #pragma mark - Accessors
44 |
45 | - (instancetype)initWithCoder:(NSCoder *)aDecoder {
46 |
47 | self = [super initWithCoder:aDecoder];
48 |
49 | if (self) [self initCompass];
50 |
51 | return self;
52 | }
53 |
54 | - (instancetype)initWithFrame:(CGRect)frame {
55 |
56 | self = [super initWithFrame:frame];
57 |
58 | if (self) [self initCompass];
59 |
60 | return self;
61 | }
62 |
63 | - (void)initCompass {
64 |
65 | #if TARGET_IPHONE_SIMULATOR
66 | _fieldOfView = 50.0;
67 | #endif
68 |
69 | _labelFont = [UIFont boldSystemFontOfSize:24.0];
70 | _labelColor = [UIColor whiteColor];
71 |
72 | _northColor = [UIColor redColor];
73 | _northLineWidth = 4.0;
74 |
75 | _topScaleColor = [UIColor whiteColor];
76 | _topScaleLineWidth = 2.0;
77 |
78 | _bottomScaleColor = [UIColor whiteColor];
79 | _bottomScaleLineWidth = 2.0;
80 | }
81 |
82 | #pragma mark - Accessors
83 |
84 | - (void)setHeadingAngle:(CGFloat)headingAngle {
85 |
86 | _headingAngle = headingAngle;
87 |
88 | [self setNeedsDisplay];
89 | }
90 |
91 | - (void)setFieldOfView:(CGFloat)fieldOfView {
92 |
93 | _fieldOfView = fieldOfView;
94 |
95 | [self setNeedsDisplay];
96 | }
97 |
98 | - (void)setLabelFont:(UIFont *)labelFont {
99 |
100 | _labelFont = [labelFont copy];
101 |
102 | [self setNeedsDisplay];
103 | }
104 |
105 | - (void)setLabelColor:(UIColor *)labelColor {
106 |
107 | _labelColor = [labelColor copy];
108 |
109 | [self setNeedsDisplay];
110 | }
111 |
112 | - (void)setNorthColor:(UIColor *)northColor {
113 |
114 | _northColor = [northColor copy];
115 |
116 | [self setNeedsDisplay];
117 | }
118 |
119 | - (void)setNorthLineWidth:(CGFloat)northLineWidth {
120 |
121 | _northLineWidth = northLineWidth;
122 |
123 | [self setNeedsDisplay];
124 | }
125 |
126 | - (void)setTopScaleColor:(UIColor *)topScaleColor {
127 |
128 | _topScaleColor = [topScaleColor copy];
129 |
130 | [self setNeedsDisplay];
131 | }
132 |
133 | - (void)setTopScaleLineWidth:(CGFloat)topScaleLineWidth {
134 |
135 | _topScaleLineWidth = topScaleLineWidth;
136 |
137 | [self setNeedsDisplay];
138 | }
139 |
140 | - (void)setBottomScaleColor:(UIColor *)bottomScaleColor {
141 |
142 | _bottomScaleColor = [bottomScaleColor copy];
143 |
144 | [self setNeedsDisplay];
145 | }
146 |
147 | - (void)setBottomScaleLineWidth:(CGFloat)bottomScaleLineWidth {
148 |
149 | _bottomScaleLineWidth = bottomScaleLineWidth;
150 |
151 | [self setNeedsDisplay];
152 | }
153 |
154 | - (NSDictionary *)compassLabels {
155 |
156 | if (_compassLabels == nil) {
157 |
158 | _compassLabels = @{ @(000.0): NSLocalizedString(@"N", nil), @(045.0): NSLocalizedString(@"NE", nil),
159 | @(090.0): NSLocalizedString(@"E", nil), @(135.0): NSLocalizedString(@"SE", nil),
160 | @(180.0): NSLocalizedString(@"S", nil), @(225.0): NSLocalizedString(@"SW", nil),
161 | @(270.0): NSLocalizedString(@"W", nil), @(315.0): NSLocalizedString(@"NW", nil) };
162 | }
163 |
164 | return _compassLabels;
165 | }
166 |
167 | #pragma mark - Drawing
168 |
169 | #define TOPSCALE 16
170 | #define LABELSCALE 8
171 | #define BOTTOMSCALE 72
172 |
173 | - (void)drawRect:(CGRect)rect {
174 |
175 | [self.backgroundColor setFill];
176 | UIRectFill(self.bounds);
177 |
178 | UIBezierPath *line = [UIBezierPath bezierPath];
179 |
180 | CGFloat width = CGRectGetWidth(self.bounds);
181 | CGFloat width_2 = 0.5 * width;
182 | CGFloat height = CGRectGetHeight(self.bounds);
183 | CGFloat height_2 = 0.5 * height;
184 |
185 | [line moveToPoint:CGPointMake(width_2, 0.0)];
186 | [line addLineToPoint:CGPointMake(width_2, height)];
187 |
188 | CGFloat fov_2 = 0.5 * self.fieldOfView;
189 | CGFloat factor = CGRectGetWidth(self.bounds) / tanf(GLKMathDegreesToRadians(fov_2));
190 | CGFloat startAngle = self.headingAngle - fov_2;
191 | CGFloat endAngle = self.headingAngle + fov_2 + 2.0;
192 |
193 | if (startAngle < 0.0) {
194 |
195 | startAngle += 360.0;
196 | endAngle += 360.0;
197 | }
198 |
199 | CGContextRef context = UIGraphicsGetCurrentContext();
200 |
201 | // |(-fov/2) | (+fov/2)|
202 | // |<----------------+---------------->|
203 | // |-w/2 0 +w/2|
204 | //
205 |
206 | // Top scale
207 | {
208 | [self.topScaleColor setStroke];
209 |
210 | CGFloat incr = 360.0 / TOPSCALE;
211 | NSInteger index = floorf(startAngle / incr);
212 |
213 | for (CGFloat angle = incr * index; angle <= endAngle; angle += incr, index++) {
214 |
215 | CGContextSaveGState(context);
216 |
217 | if (index % TOPSCALE) {
218 |
219 | line.lineWidth = self.topScaleLineWidth;
220 |
221 | } else {
222 |
223 | [self.northColor setStroke];
224 | line.lineWidth = self.northLineWidth;
225 | }
226 |
227 | CGFloat delta = GLKMathDegreesToRadians(angle - self.headingAngle);
228 | CGFloat xoffset = factor * tanf(0.5 * delta);
229 | CGFloat yscale = 0.25;
230 |
231 | CGContextScaleCTM(context, 1.0, yscale);
232 | CGContextTranslateCTM(context, xoffset, 0.0);
233 |
234 | [line stroke];
235 |
236 | CGContextRestoreGState(context);
237 | }
238 | }
239 |
240 | // Label scale
241 | {
242 | CGFloat incr = 360.0 / LABELSCALE;
243 | NSInteger index = floorf(startAngle / incr);
244 |
245 | for (CGFloat angle = incr * index; angle <= endAngle; angle += incr, index++) {
246 |
247 | NSString *label = self.compassLabels[@(incr * (index % LABELSCALE))];
248 |
249 | if (label) {
250 |
251 | CGContextSaveGState(context);
252 |
253 | CGFloat delta = GLKMathDegreesToRadians(angle - self.headingAngle);
254 | CGFloat xoffset = factor * tanf(0.5 * delta);
255 |
256 | CGContextTranslateCTM(context, xoffset, 0.0);
257 |
258 | UIColor *labelColor = (index % LABELSCALE) ? self.labelColor : self.northColor;
259 | NSDictionary *labelAttributes = @{ NSFontAttributeName: self.labelFont, NSForegroundColorAttributeName: labelColor };
260 | CGSize labelSize = [label sizeWithAttributes:labelAttributes];
261 |
262 | [label drawAtPoint:CGPointMake(round(width_2 - 0.5 * labelSize.width), round(height_2 - 0.5 * labelSize.height)) withAttributes:labelAttributes];
263 |
264 | CGContextRestoreGState(context);
265 | }
266 | }
267 | }
268 |
269 | // Bottom scale
270 | {
271 | [self.bottomScaleColor setStroke];
272 |
273 | CGFloat incr = 360.0 / BOTTOMSCALE;
274 | NSInteger index = floorf(startAngle / incr);
275 |
276 | for (CGFloat angle = incr * index; angle <= endAngle; angle += incr, index++) {
277 |
278 | CGContextSaveGState(context);
279 |
280 | if (index % BOTTOMSCALE) {
281 |
282 | line.lineWidth = self.bottomScaleLineWidth;
283 |
284 | } else {
285 |
286 | [self.northColor setStroke];
287 | line.lineWidth = self.northLineWidth;
288 | }
289 |
290 | CGFloat delta = GLKMathDegreesToRadians(angle - self.headingAngle);
291 | CGFloat xoffset = factor * tanf(0.5 * delta);
292 | CGFloat yscale = 0.25;
293 |
294 | CGContextScaleCTM(context, 1.0, yscale);
295 | CGContextTranslateCTM(context, xoffset, 0.75 * height / yscale);
296 |
297 | [line stroke];
298 |
299 | CGContextRestoreGState(context);
300 | }
301 | }
302 | }
303 |
304 | #pragma mark - Interface Builder
305 |
306 | - (void)prepareForInterfaceBuilder {
307 |
308 | [super prepareForInterfaceBuilder];
309 |
310 | self.headingAngle = 0;
311 | self.fieldOfView = 50;
312 | }
313 |
314 | @end
315 |
--------------------------------------------------------------------------------
/TGLAugmentedRealityView/TGLARImageShape.h:
--------------------------------------------------------------------------------
1 | //
2 | // TGLARImageShape.h
3 | // TGLAugmentedRealityView
4 | //
5 | // Created by Tim Gleue on 12.11.15.
6 | // Copyright (c) 2015 PowerMobile Team (https://powermobile.org)
7 | //
8 | // Permission is hereby granted, free of charge, to any person obtaining a copy
9 | // of this software and associated documentation files (the "Software"), to deal
10 | // in the Software without restriction, including without limitation the rights
11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 | // copies of the Software, and to permit persons to whom the Software is
13 | // furnished to do so, subject to the following conditions:
14 | //
15 | // The above copyright notice and this permission notice shall be included in
16 | // all copies or substantial portions of the Software.
17 | //
18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 | // THE SOFTWARE.
25 |
26 | #import "TGLARShapeOverlay.h"
27 |
28 | /// A rectangular 3D shape in Y/Z plane showing an image texture and facing the positive X axis.
29 | @interface TGLARImageShape : TGLARShapeOverlay
30 |
31 | /** Designated intializer.
32 | *
33 | * @param context OpenGL ES context to create shape in.
34 | * @param size Shape width and height in meters.
35 | * @param image The Image to apply as shape's texture.
36 | *
37 | * @return An initilized instance or nil if initialization fails.
38 | */
39 | - (nullable instancetype)initWithContext:(nonnull EAGLContext *)context size:(CGSize)size image:(nullable UIImage *)image;
40 |
41 | /** Set the shape's texture image.
42 | *
43 | * @param image The Image to apply as shape's texture.
44 | *
45 | * @return YES on success, NO if texture loading fails.
46 | */
47 | - (BOOL)setImage:(nullable UIImage *)image;
48 |
49 | @end
50 |
--------------------------------------------------------------------------------
/TGLAugmentedRealityView/TGLARImageShape.m:
--------------------------------------------------------------------------------
1 | //
2 | // TGLARImageShape.m
3 | // TGLAugmentedRealityView
4 | //
5 | // Created by Tim Gleue on 12.11.15.
6 | // Copyright (c) 2015 PowerMobile Team (https://powermobile.org)
7 | //
8 | // Permission is hereby granted, free of charge, to any person obtaining a copy
9 | // of this software and associated documentation files (the "Software"), to deal
10 | // in the Software without restriction, including without limitation the rights
11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 | // copies of the Software, and to permit persons to whom the Software is
13 | // furnished to do so, subject to the following conditions:
14 | //
15 | // The above copyright notice and this permission notice shall be included in
16 | // all copies or substantial portions of the Software.
17 | //
18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 | // THE SOFTWARE.
25 |
26 | #import "TGLARImageShape.h"
27 |
28 | // GL data
29 | //
30 | typedef struct {
31 |
32 | float Position[3];
33 | float Texture[2];
34 | float Normal[3];
35 |
36 | } Vertex;
37 |
38 | static const Vertex Vertices[] = {
39 |
40 | { { 0, +1, -1 }, { 1, 1 }, { 1, 0, 0 } },
41 | { { 0, +1, +1 }, { 1, 0 }, { 1, 0, 0 } },
42 | { { 0, -1, +1 }, { 0, 0 }, { 1, 0, 0 } },
43 | { { 0, -1, -1 }, { 0, 1 }, { 1, 0, 0 } }
44 | };
45 |
46 | static const GLubyte Indices[] = { 0, 1, 2, 2, 3, 0 };
47 |
48 | static GLuint indexBuffer = 0;
49 | static NSUInteger bufferCount = 0;
50 |
51 | @interface TGLARImageShape () {
52 |
53 | GLuint _vertexBuffer;
54 | GLuint _indexBuffer;
55 | };
56 |
57 | @property (strong, nonatomic) GLKTextureInfo *textureInfo;
58 |
59 | @end
60 |
61 | @implementation TGLARImageShape
62 |
63 | - (instancetype)initWithContext:(EAGLContext *)context size:(CGSize)size image:(UIImage *)image {
64 |
65 | self = [super initWithContext:context];
66 |
67 | if (self) {
68 |
69 | self.effect.constantColor = GLKVector4Make(1.0, 1.0, 1.0, 1.0);
70 |
71 | self.image = image;
72 |
73 | float w2 = 0.5 * size.width;
74 | float h2 = 0.5 * size.height;
75 |
76 | static Vertex bgVertices[4];
77 |
78 | for (NSUInteger idx = 0; idx < 4; idx++) {
79 |
80 | bgVertices[idx].Position[0] = Vertices[idx].Position[0];
81 | bgVertices[idx].Position[1] = Vertices[idx].Position[1] * w2;
82 | bgVertices[idx].Position[2] = Vertices[idx].Position[2] * h2;
83 |
84 | bgVertices[idx].Texture[0] = Vertices[idx].Texture[0];
85 | bgVertices[idx].Texture[1] = Vertices[idx].Texture[1];
86 |
87 | bgVertices[idx].Normal[0] = Vertices[idx].Normal[0];
88 | bgVertices[idx].Normal[1] = Vertices[idx].Normal[1];
89 | bgVertices[idx].Normal[2] = Vertices[idx].Normal[2];
90 | }
91 |
92 | glGenBuffers(1, &_vertexBuffer);
93 | glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer);
94 | glBufferData(GL_ARRAY_BUFFER, sizeof(bgVertices), bgVertices, GL_STATIC_DRAW);
95 | glBindBuffer(GL_ARRAY_BUFFER, 0);
96 |
97 | if (indexBuffer == 0) {
98 |
99 | glGenBuffers(1, &indexBuffer);
100 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
101 | glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(Indices), Indices, GL_STATIC_DRAW);
102 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
103 | }
104 |
105 | ++bufferCount;
106 | }
107 |
108 | return self;
109 | }
110 |
111 | - (void)dealloc {
112 |
113 | if (self.context) {
114 |
115 | [EAGLContext setCurrentContext:self.context];
116 |
117 | [self freeImage];
118 |
119 | glDeleteBuffers(1, &_vertexBuffer); _vertexBuffer = 0;
120 |
121 | if (bufferCount > 0) --bufferCount;
122 |
123 | if (bufferCount == 0) {
124 |
125 | glDeleteBuffers(1, &indexBuffer);
126 | indexBuffer = 0;
127 | }
128 | }
129 | }
130 |
131 | #pragma mark - Methods
132 |
133 | - (BOOL)setImage:(UIImage *)image {
134 |
135 | [EAGLContext setCurrentContext:self.context];
136 |
137 | [self freeImage];
138 |
139 | if (image == nil) return YES;
140 |
141 | NSError *error = nil;
142 |
143 | GLKTextureInfo *textureInfo = [GLKTextureLoader textureWithCGImage:image.CGImage options:nil error:&error];
144 |
145 | if (textureInfo) {
146 |
147 | self.textureInfo = textureInfo;
148 |
149 | self.effect.texture2d0.name = self.textureInfo.name;
150 | self.effect.texture2d0.target = self.textureInfo.target;
151 | self.effect.texture2d0.envMode = GLKTextureEnvModeReplace;
152 | self.effect.texture2d0.enabled = GL_TRUE;
153 |
154 | return YES;
155 |
156 | } else {
157 |
158 | NSLog(@"%s Texture image could not be loaded: %@", __PRETTY_FUNCTION__, error.localizedDescription);
159 |
160 | self.effect.texture2d0.enabled = GL_FALSE;
161 |
162 | return NO;
163 | }
164 | }
165 |
166 | - (BOOL)draw {
167 |
168 | if (![super draw]) return NO;
169 |
170 | glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer);
171 | glEnableVertexAttribArray(GLKVertexAttribPosition);
172 | glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (const GLvoid *)offsetof(Vertex, Position));
173 | glEnableVertexAttribArray(GLKVertexAttribTexCoord0);
174 | glVertexAttribPointer(GLKVertexAttribTexCoord0, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (const GLvoid *)offsetof(Vertex, Texture));
175 | glEnableVertexAttribArray(GLKVertexAttribNormal);
176 | glVertexAttribPointer(GLKVertexAttribNormal, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (const GLvoid *)offsetof(Vertex, Normal));
177 |
178 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
179 |
180 | glDrawElements(GL_TRIANGLES, sizeof(Indices)/sizeof(Indices[0]), GL_UNSIGNED_BYTE, 0);
181 |
182 | glDisableVertexAttribArray(GLKVertexAttribTexCoord0);
183 | glDisableVertexAttribArray(GLKVertexAttribNormal);
184 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
185 | glBindBuffer(GL_ARRAY_BUFFER, 0);
186 |
187 | return YES;
188 | }
189 |
190 | - (BOOL)drawUsingConstantColor:(GLKVector4)color {
191 |
192 | GLKVector4 constantColor = self.effect.constantColor;
193 | GLboolean useConstantColor = self.effect.useConstantColor;
194 | GLboolean texture2d0Enabled = self.effect.texture2d0.enabled;
195 |
196 | self.effect.constantColor = color;
197 | self.effect.useConstantColor = GL_TRUE;
198 | self.effect.texture2d0.enabled = GL_FALSE;
199 |
200 | BOOL ok = [super drawUsingConstantColor:color];
201 |
202 | if (ok) {
203 |
204 | glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer);
205 | glEnableVertexAttribArray(GLKVertexAttribPosition);
206 | glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (const GLvoid *)offsetof(Vertex, Position));
207 | glEnableVertexAttribArray(GLKVertexAttribNormal);
208 | glVertexAttribPointer(GLKVertexAttribNormal, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (const GLvoid *)offsetof(Vertex, Normal));
209 |
210 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
211 |
212 | glDrawElements(GL_TRIANGLES, sizeof(Indices)/sizeof(Indices[0]), GL_UNSIGNED_BYTE, 0);
213 |
214 | glDisableVertexAttribArray(GLKVertexAttribNormal);
215 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
216 | glBindBuffer(GL_ARRAY_BUFFER, 0);
217 | }
218 |
219 | self.effect.texture2d0.enabled = texture2d0Enabled;
220 | self.effect.useConstantColor = useConstantColor;
221 | self.effect.constantColor = constantColor;
222 |
223 | return ok;
224 | }
225 |
226 | #pragma mark - Helpers
227 |
228 | - (void)freeImage {
229 |
230 | if (self.textureInfo) {
231 |
232 | // KLUDGE: Get rid of previous texture memory,
233 | // but there seems to be no official
234 | // way to release a GLKTextureInfo
235 | // allocated by GLKTextureLoader...
236 | //
237 | // See: http://stackoverflow.com/a/8720298
238 | //
239 | GLuint name = self.textureInfo.name;
240 |
241 | glDeleteTextures(1, &name);
242 |
243 | self.textureInfo = nil;
244 | }
245 |
246 | self.effect.texture2d0.enabled = GL_FALSE;
247 | }
248 |
249 | @end
250 |
--------------------------------------------------------------------------------
/TGLAugmentedRealityView/TGLAROverlay.h:
--------------------------------------------------------------------------------
1 | //
2 | // TGLAROverlay.h
3 | // TGLAugmentedRealityView
4 | //
5 | // Created by Tim Gleue on 09.11.15.
6 | // Copyright (c) 2015 PowerMobile Team (https://powermobile.org)
7 | //
8 | // Permission is hereby granted, free of charge, to any person obtaining a copy
9 | // of this software and associated documentation files (the "Software"), to deal
10 | // in the Software without restriction, including without limitation the rights
11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 | // copies of the Software, and to permit persons to whom the Software is
13 | // furnished to do so, subject to the following conditions:
14 | //
15 | // The above copyright notice and this permission notice shall be included in
16 | // all copies or substantial portions of the Software.
17 | //
18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 | // THE SOFTWARE.
25 |
26 | #import
27 | #import
28 |
29 | @class TGLARViewOverlay;
30 | @class TGLARShapeOverlay;
31 |
32 | /// An object returned by a @p TGLARViewDataSource must adopt this protocol.
33 | @protocol TGLAROverlay
34 |
35 | /** Returns the X/Y/Z position this overlay is attached to.
36 | *
37 | * The position is defined relative to the camera, which is
38 | * located at the coordinate system origin. The distance
39 | * values are given in meters.
40 | *
41 | * The coordinate system is right-handed with positive X axis
42 | * pointing north, the postive Y axis pointing east and the
43 | * positive Z axis pointing upwards.
44 | *
45 | * @return The X/Y/Z position of the overlay target.
46 | */
47 | - (GLKVector3)targetPosition;
48 |
49 | /** Set the X/Y/Z position this overlay should be attached to.
50 | *
51 | * The distance values have to be given in meters.
52 | *
53 | * @param position The the overlay target X/Y/Z position.
54 | *
55 | * @sa -targetPosition
56 | */
57 | - (void)setTargetPosition:(GLKVector3)position;
58 |
59 | @optional;
60 |
61 | /** Returns the view to show for this overlay.
62 | *
63 | * If the receiver does not respond to this selector
64 | * no view is shown for the overlay.
65 | *
66 | * @return A TGLARViewOverlay instance.
67 | *
68 | * @sa TGLARViewOverlay
69 | */
70 | - (nullable TGLARViewOverlay *)overlayView;
71 |
72 | /** Returns the 3D shape to show for this overlay.
73 | *
74 | * If the receiver does not respond to this selector
75 | * no shape is shown for the overlay.
76 | *
77 | * @return A TGLARShapeOverlay instance.
78 | *
79 | * @sa TGLARShapeOverlay
80 | */
81 | - (nullable TGLARShapeOverlay *)overlayShape;
82 |
83 | @end
84 |
--------------------------------------------------------------------------------
/TGLAugmentedRealityView/TGLAROverlayContainerView.h:
--------------------------------------------------------------------------------
1 | //
2 | // TGLAROverlayContainerView.h
3 | // TGLAugmentedRealityView
4 | //
5 | // Created by Tim Gleue on 17.11.15.
6 | // Copyright (c) 2015 PowerMobile Team (https://powermobile.org)
7 | //
8 | // Permission is hereby granted, free of charge, to any person obtaining a copy
9 | // of this software and associated documentation files (the "Software"), to deal
10 | // in the Software without restriction, including without limitation the rights
11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 | // copies of the Software, and to permit persons to whom the Software is
13 | // furnished to do so, subject to the following conditions:
14 | //
15 | // The above copyright notice and this permission notice shall be included in
16 | // all copies or substantial portions of the Software.
17 | //
18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 | // THE SOFTWARE.
25 |
26 | #import
27 | #import
28 |
29 | #import "TGLARViewOverlay.h"
30 |
31 | /// A @p UIView subclass used internally to position the overlay shapes on a @p TGLARView.
32 | @interface TGLAROverlayContainerView : UIView
33 |
34 | /// The view containing the all of the overlay views.
35 | @property (nonatomic, readonly, nonnull) UIView *contentView;
36 |
37 | /// The current transformation derived from device attitude.
38 | @property (nonatomic, assign) GLKMatrix4 overlayTransformation;
39 |
40 | /// An array of @p TGLARViewOverlay objects to be layout out.
41 | @property (nonatomic, strong, nullable) NSArray *overlayViews;
42 |
43 | @end
44 |
--------------------------------------------------------------------------------
/TGLAugmentedRealityView/TGLAROverlayContainerView.m:
--------------------------------------------------------------------------------
1 | //
2 | // TGLAROverlayContainerView.m
3 | // TGLAugmentedRealityView
4 | //
5 | // Created by Tim Gleue on 17.11.15.
6 | // Copyright (c) 2015 PowerMobile Team (https://powermobile.org)
7 | //
8 | // Permission is hereby granted, free of charge, to any person obtaining a copy
9 | // of this software and associated documentation files (the "Software"), to deal
10 | // in the Software without restriction, including without limitation the rights
11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 | // copies of the Software, and to permit persons to whom the Software is
13 | // furnished to do so, subject to the following conditions:
14 | //
15 | // The above copyright notice and this permission notice shall be included in
16 | // all copies or substantial portions of the Software.
17 | //
18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 | // THE SOFTWARE.
25 |
26 | #import "TGLAROverlayContainerView.h"
27 |
28 | #import
29 |
30 | @implementation TGLAROverlayContainerView
31 |
32 | - (instancetype)initWithFrame:(CGRect)frame {
33 |
34 | self = [super initWithFrame:frame];
35 |
36 | if (self) [self initContainer];
37 |
38 | return self;
39 | }
40 |
41 | - (instancetype)initWithCoder:(NSCoder *)aDecoder {
42 |
43 | self = [super initWithCoder:aDecoder];
44 |
45 | if (self) [self initContainer];
46 |
47 | return self;
48 | }
49 |
50 | - (void)initContainer {
51 |
52 | _contentView = [[UIView alloc] init];
53 | _contentView.backgroundColor = [UIColor clearColor];
54 | _contentView.opaque = NO;
55 |
56 | [self addSubview:_contentView];
57 | }
58 |
59 | #pragma mark - Accessors
60 |
61 | - (void)setOverlayViews:(NSArray *)overlayViews {
62 |
63 | for (TGLARViewOverlay *view in self.overlayViews) {
64 |
65 | [view removeFromSuperview];
66 | }
67 |
68 | _overlayViews = overlayViews;
69 |
70 | [self setNeedsLayout];
71 | }
72 |
73 | - (void)setOverlayTransformation:(GLKMatrix4)overlayTransformation {
74 |
75 | _overlayTransformation = overlayTransformation;
76 |
77 | [self setNeedsLayout];
78 | }
79 |
80 | #pragma mark - Layout
81 |
82 | - (void)layoutSubviews {
83 |
84 | [super layoutSubviews];
85 |
86 | CGRect bounds = self.bounds;
87 | CGSize offset = CGSizeZero;
88 |
89 | if (@available(iOS 11, *)) {
90 |
91 | CGRect safeBounds = CGRectMake(bounds.origin.x + self.safeAreaInsets.left, bounds.origin.y + self.safeAreaInsets.top, bounds.size.width - self.safeAreaInsets.left - self.safeAreaInsets.right, bounds.size.height - self.safeAreaInsets.top - self.safeAreaInsets.bottom);;
92 |
93 | self.contentView.frame = safeBounds;
94 |
95 | offset.width -= 0.5 * self.safeAreaInsets.left;
96 | offset.width += 0.5 * self.safeAreaInsets.right;
97 |
98 | offset.height -= 0.5 * self.safeAreaInsets.top;
99 | offset.height += 0.5 * self.safeAreaInsets.bottom;
100 |
101 | } else {
102 |
103 | self.contentView.frame = bounds;
104 | }
105 |
106 | // Perform 3D viewing transformation and clip invisible overlays
107 | //
108 | NSMutableArray *visibleViews = [NSMutableArray array];
109 |
110 | for (TGLARViewOverlay *view in self.overlayViews) {
111 |
112 | GLKVector3 targetPosition = [view.overlay targetPosition];
113 | GLKVector4 positionVector = GLKVector4MakeWithVector3(targetPosition, 1.0);
114 | GLKVector4 homoVector = GLKMatrix4MultiplyVector4(self.overlayTransformation, positionVector);
115 |
116 | view.viewPosition = GLKVector3Make(homoVector.x / homoVector.w, homoVector.y / homoVector.w, homoVector.z / homoVector.w);
117 |
118 | GLKVector2 unitPosition = GLKVector2Make(view.viewPosition.x, view.viewPosition.y);
119 | float unitLength = GLKVector2Length(unitPosition);
120 |
121 | if (unitLength < 2.0 && view.viewPosition.z <= 1.0) {
122 |
123 | [visibleViews addObject:view];
124 |
125 | } else {
126 |
127 | view.hidden = YES;
128 | view.calloutLength = 0.0;
129 | }
130 |
131 | [view removeFromSuperview];
132 | }
133 |
134 | // Arrange n visible overlays from back (0) to front (n-1)
135 | //
136 | [visibleViews sortUsingComparator:^NSComparisonResult (TGLARViewOverlay *view1, TGLARViewOverlay *view2) {
137 |
138 | if (view1.viewPosition.z > view2.viewPosition.z) return (NSComparisonResult)NSOrderedAscending;
139 | if (view1.viewPosition.z < view2.viewPosition.z) return (NSComparisonResult)NSOrderedDescending;
140 |
141 | return (NSComparisonResult)NSOrderedSame;
142 | }];
143 |
144 | for (TGLARViewOverlay *view in visibleViews) [self.contentView addSubview:view];
145 |
146 | // Position overlays in container and minimize overlap
147 | //
148 | CGFloat calloutLength = 0.0;
149 | CGFloat calloutOffset = 30.0;
150 | CGFloat calloutDefault = 120.0;
151 |
152 | for (NSInteger idx = visibleViews.count - 1; idx >= 0; idx--) {
153 |
154 | TGLARViewOverlay *view = visibleViews[idx];
155 |
156 | GLKVector2 unitPosition = GLKVector2Make(view.viewPosition.x, view.viewPosition.y);
157 | float unitLength = GLKVector2Length(unitPosition);
158 |
159 | CGPoint screenPosition;
160 |
161 | screenPosition.x = round(0.5 * (unitPosition.x + 1.0) * CGRectGetWidth(self.contentView.bounds)) + offset.width;
162 | screenPosition.y = round(0.5 * (1.0 - unitPosition.y) * CGRectGetHeight(self.contentView.bounds)) + offset.height;
163 |
164 | if (view.hidden) {
165 |
166 | // Newly appearing view needs length
167 | //
168 | if (calloutLength > 0.0) {
169 |
170 | view.calloutLength = ++calloutLength;
171 |
172 | } else {
173 |
174 | view.calloutLength = calloutLength = calloutDefault;
175 | }
176 |
177 | view.rightAligned = (unitPosition.x > 0.0);
178 | view.hidden = NO;
179 |
180 | } else {
181 |
182 | if (calloutLength == 0.0) {
183 |
184 | if (view.calloutLength > calloutDefault) {
185 |
186 | view.calloutLength--;
187 |
188 | } else if (view.calloutLength < calloutDefault) {
189 |
190 | view.calloutLength++;
191 | }
192 |
193 | } else if ((view.calloutLength - calloutLength) > calloutOffset) {
194 |
195 | view.calloutLength--;
196 |
197 | } else if ((view.calloutLength - calloutLength) < calloutOffset) {
198 |
199 | view.calloutLength++;
200 | }
201 |
202 | calloutLength = view.calloutLength;
203 | }
204 |
205 | [view sizeToFit];
206 |
207 | CGRect frame = view.bounds;
208 |
209 | frame.origin.x = screenPosition.x;
210 |
211 | if (view.rightAligned) frame.origin.x -= CGRectGetWidth(frame);
212 |
213 | frame.origin.y = screenPosition.y;
214 |
215 | if (!view.upsideDown) frame.origin.y -= CGRectGetHeight(frame);
216 |
217 | view.frame = frame;
218 | view.alpha = (unitLength > 1.0) ? MAX(2.0 - unitLength, 0.0) : 1.0;
219 |
220 | [view setNeedsDisplay];
221 | }
222 | }
223 |
224 | #pragma mark - Interaction
225 |
226 | - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
227 |
228 | if ([self pointInside:point withEvent:event] && self.isUserInteractionEnabled && !self.isHidden && self.alpha > 0.01) {
229 |
230 | for (UIView *subview in [self.contentView.subviews reverseObjectEnumerator]) {
231 |
232 | CGPoint convertedPoint = [subview convertPoint:point fromView:self];
233 | UIView *hitTestView = [subview hitTest:convertedPoint withEvent:event];
234 |
235 | if (hitTestView) return hitTestView;
236 | }
237 |
238 | return self;
239 | }
240 |
241 | return nil;
242 | }
243 |
244 | @end
245 |
--------------------------------------------------------------------------------
/TGLAugmentedRealityView/TGLARShapeOverlay.h:
--------------------------------------------------------------------------------
1 | //
2 | // TGLARShapeOverlay.h
3 | // TGLAugmentedRealityView
4 | //
5 | // Created by Tim Gleue on 09.11.15.
6 | // Copyright (c) 2015 PowerMobile Team (https://powermobile.org)
7 | //
8 | // Permission is hereby granted, free of charge, to any person obtaining a copy
9 | // of this software and associated documentation files (the "Software"), to deal
10 | // in the Software without restriction, including without limitation the rights
11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 | // copies of the Software, and to permit persons to whom the Software is
13 | // furnished to do so, subject to the following conditions:
14 | //
15 | // The above copyright notice and this permission notice shall be included in
16 | // all copies or substantial portions of the Software.
17 | //
18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 | // THE SOFTWARE.
25 |
26 | #import
27 | #import
28 |
29 | #import "TGLAROverlay.h"
30 |
31 | /** An object to present 3D content in a @p TGLARView.
32 | *
33 | * The containing @p TGLARView sets the projection and
34 | * modelview matrices depending on the current device
35 | * orientation and attitude.
36 | *
37 | * A custom @p -transform is pre-multipled to the modelview
38 | * matrix to allow for individual shape tranformations before
39 | * the viewing transformations are applied.
40 | */
41 | @interface TGLARShapeOverlay : NSObject
42 |
43 | /// The overlay this shape belongs to.
44 | @property (nonatomic, weak, nullable) id overlay;
45 |
46 | /// The shape's OpenGL ES rendering context.
47 | @property (nonatomic, weak, nullable) EAGLContext * context;
48 | /// The shape transformation matrix pre-multiplied to @p -viewMatrix.
49 | @property (nonatomic, assign) GLKMatrix4 transform;
50 | /// The shape's GLKKit rendering effect. @sa GLKBaseEffect for details.
51 | @property (nonatomic, readonly, nonnull) GLKBaseEffect *effect;
52 |
53 | /// The OpenGL ES view transformation to be applied. Set by the containing @p TGLARView.
54 | @property (nonatomic, assign) GLKMatrix4 viewMatrix;
55 | /// The OpenGL ES projection transformation to be applied. Set by the containing @p TGLARView.
56 | @property (nonatomic, assign) GLKMatrix4 projectionMatrix;
57 |
58 | /// Initialize an instance using the given OpenGL ES context.
59 | - (nullable instancetype)initWithContext:(nonnull EAGLContext *)context;
60 |
61 | /** Draws the shape using OpenGL ES.
62 | *
63 | * The method implementation initializes the transform property
64 | * of the @p -effect and calls its @p -prepareToDraw method.
65 | *
66 | * Override this method in subclasses to customize the shape.
67 | * Unless the subclass takes care of setting up the effect as
68 | * described above, the subcalls implementation @a must call
69 | * @p [super draw].
70 | *
71 | * @return YES if drawing succeeds. NO otherwise, e.g. if -context is no longer valid.
72 | */
73 | - (BOOL)draw;
74 |
75 | /** Draws the shape using OpenGL ES without any shading and texturing.
76 | *
77 | * The base class implementation simply calls @[self -draw].
78 | *
79 | * This method is used internally to implement picking shapes
80 | * on an @p TGLARView.
81 | *
82 | * @sa @p TGLARView
83 | */
84 | - (BOOL)drawUsingConstantColor:(GLKVector4)color;
85 |
86 | @end
87 |
--------------------------------------------------------------------------------
/TGLAugmentedRealityView/TGLARShapeOverlay.m:
--------------------------------------------------------------------------------
1 | //
2 | // TGLARShapeOverlay.m
3 | // TGLAugmentedRealityView
4 | //
5 | // Created by Tim Gleue on 09.11.15.
6 | // Copyright (c) 2015 PowerMobile Team (https://powermobile.org)
7 | //
8 | // Permission is hereby granted, free of charge, to any person obtaining a copy
9 | // of this software and associated documentation files (the "Software"), to deal
10 | // in the Software without restriction, including without limitation the rights
11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 | // copies of the Software, and to permit persons to whom the Software is
13 | // furnished to do so, subject to the following conditions:
14 | //
15 | // The above copyright notice and this permission notice shall be included in
16 | // all copies or substantial portions of the Software.
17 | //
18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 | // THE SOFTWARE.
25 |
26 | #import "TGLARShapeOverlay.h"
27 |
28 | @interface TGLARShapeOverlay ()
29 |
30 | @property (nonatomic, strong) GLKTextureInfo *textureInfo;
31 |
32 | @end
33 |
34 | @implementation TGLARShapeOverlay
35 |
36 | - (instancetype)initWithContext:(EAGLContext *)context {
37 |
38 | self = [super init];
39 |
40 | if (self) {
41 |
42 | _context = context;
43 |
44 | [EAGLContext setCurrentContext:self.context];
45 |
46 | _effect = [[GLKBaseEffect alloc] init];
47 | _transform = GLKMatrix4Identity;
48 | }
49 |
50 | return self;
51 | }
52 |
53 | - (void)dealloc {
54 |
55 | if (self.context) {
56 |
57 | [EAGLContext setCurrentContext:self.context];
58 |
59 | self.context = nil;
60 | }
61 | }
62 |
63 | #pragma mark - Methods
64 |
65 | - (BOOL)draw {
66 |
67 | if (!self.context) return NO;
68 |
69 | GLKVector3 targetPosition = self.overlay.targetPosition;
70 | GLKMatrix4 positionMatrix = GLKMatrix4MakeTranslation(targetPosition.x, targetPosition.y, targetPosition.z);
71 | GLKMatrix4 modelMatrix = GLKMatrix4Multiply(positionMatrix, self.transform);
72 |
73 | self.effect.transform.modelviewMatrix = GLKMatrix4Multiply(self.viewMatrix, modelMatrix);
74 | self.effect.transform.projectionMatrix = self.projectionMatrix;
75 |
76 | [self.effect prepareToDraw];
77 |
78 | return YES;
79 | }
80 |
81 | - (BOOL)drawUsingConstantColor:(GLKVector4)color {
82 |
83 | return [self draw];
84 | }
85 |
86 | @end
87 |
--------------------------------------------------------------------------------
/TGLAugmentedRealityView/TGLARView.h:
--------------------------------------------------------------------------------
1 | //
2 | // TGLARView.h
3 | // TGLAugmentedRealityView
4 | //
5 | // Created by Tim Gleue on 09.11.15.
6 | // Copyright (c) 2015 PowerMobile Team (https://powermobile.org)
7 | //
8 | // Permission is hereby granted, free of charge, to any person obtaining a copy
9 | // of this software and associated documentation files (the "Software"), to deal
10 | // in the Software without restriction, including without limitation the rights
11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 | // copies of the Software, and to permit persons to whom the Software is
13 | // furnished to do so, subject to the following conditions:
14 | //
15 | // The above copyright notice and this permission notice shall be included in
16 | // all copies or substantial portions of the Software.
17 | //
18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 | // THE SOFTWARE.
25 |
26 | #import
27 | #import
28 |
29 | #import "TGLARCompass.h"
30 | #import "TGLAROverlay.h"
31 |
32 | @class TGLARView;
33 |
34 | /// The @pTGLARView data source must adopt the @p TGLARViewDataSource protocol.
35 | @protocol TGLARViewDataSource
36 |
37 | /** Asks the data source to return the number of overlays to be shown on the AR view.
38 | *
39 | * @param arview The AR view asking for the overlay count.
40 | *
41 | * @return The number of overlays to be shown on this particular AR view.
42 | */
43 | - (NSInteger)numberOfOverlaysInARView:(nonnull TGLARView *)arview;
44 |
45 | /** Asks the data source for the overlay to be shown at a particular index.
46 | *
47 | * @param arview The AR view asking for an overlay.
48 | *
49 | * @return An object adopting the @p TGLAROverlay protocol.
50 | *
51 | * @sa @p TGLAROverlay
52 | */
53 | - (nullable id)arView:(nonnull TGLARView *)arview overlayAtIndex:(NSInteger)index;
54 |
55 | @end
56 |
57 | /// The @pTGLARView delegate must adopt the @p TGLARViewDelegate protocol.
58 | @protocol TGLARViewDelegate
59 |
60 | @optional
61 |
62 | /** Tells the delegate that the user is about to change the offsets by a pan gesture on the AR view.
63 | *
64 | * @param arview The AR view informing the delegate about the beginning updates.
65 | *
66 | * @sa @p -heightOffset
67 | * @sa @p -headingOffset
68 | * @sa @p -positionOffset
69 | */
70 | - (void)arViewWillBeginUpdatingOffsets:(nonnull TGLARView *)arview;
71 |
72 | /** Tells the delegate that the offset values properties have been updated during a pan gesture.
73 | *
74 | * @param arview The AR view informing the delegate about the offset value update.
75 | *
76 | * @sa @p -heightOffset
77 | * @sa @p -headingOffset
78 | * @sa @p -positionOffset
79 | */
80 | - (void)arViewDidUpdateOffsets:(nonnull TGLARView *)arview;
81 |
82 | /** Tells the delegate that the user ended the pan gesture.
83 | *
84 | * @param arview The AR view informing the delegate about the end of the updates.
85 | *
86 | * @sa @p -heightOffset
87 | * @sa @p -headingOffset
88 | * @sa @p -positionOffset
89 | */
90 | - (void)arViewDidEndUpdatingOffsets:(nonnull TGLARView *)arview;
91 |
92 | /** Tells the delegate that the user is about to change to video zoom by a pinch gesture on the AR view.
93 | *
94 | * @param arview The AR view informing the delegate about the beginning updates.
95 | *
96 | * @sa @p -zoomFactor
97 | */
98 | - (void)arViewWillBeginZooming:(nonnull TGLARView *)arview;
99 |
100 | /** Tells the delegate that the video zoom factore has been updated during a pinch gesture.
101 | *
102 | * @param arview The AR view informing the delegate about the zoom factor update.
103 | *
104 | * @sa @p -zoomFactor
105 | */
106 | - (void)arViewDidZoom:(nonnull TGLARView *)arview;
107 |
108 | /** Tells the delegate that the user ended the pinch gesture.
109 | *
110 | * @param arview The AR view informing the delegate about the end of the updates.
111 | * @param zoomFactor The final video zoom factor.
112 | *
113 | * @sa @p -zoomFactor
114 | */
115 | - (void)arViewDidEndZooming:(nonnull TGLARView *)arview atFactor:(CGFloat)zoomFactor;
116 |
117 | /** Tells the delegate that the user tapped a particular overlay view.
118 | *
119 | * @param arview The AR view informing the delegate about the tap.
120 | * @param overlayView The view tapped by the user.
121 | */
122 | - (void)arView:(nonnull TGLARView *)arview didTapViewOverlay:(nonnull TGLARViewOverlay *)overlayView;
123 |
124 | /** Tells the delegate that the user tapped a particular overlay shape.
125 | *
126 | * @param arview The AR view informing the delegate about the tap.
127 | * @param overlayShape The shape tapped by the user.
128 | */
129 | - (void)arView:(nonnull TGLARView *)arview didTapShapeOverlay:(nonnull TGLARShapeOverlay *)overlayShape;
130 |
131 | /** Asks the delegate for the far clipping of overlay shapes.
132 | *
133 | * If this method is not implemented by the delegate, the value
134 | * defaults to 10000.0.
135 | *
136 | * @param arview The AR view requesting the distance value.
137 | *
138 | * @return A non-negative value for the far clipping distance.
139 | */
140 | - (CGFloat)arViewShapeOverlayFarClippingDistance:(nonnull TGLARView *)arview;
141 |
142 | /** Asks the delegate for the near clipping of overlay shapes.
143 | *
144 | * If this method is not implemented by the delegate, the value
145 | * defaults to 1.0.
146 | *
147 | * @param arview The AR view requesting the distance value.
148 | *
149 | * @return A non-negative value for the near clipping distance.
150 | */
151 | - (CGFloat)arViewShapeOverlayNearClippingDistance:(nonnull TGLARView *)arview;
152 |
153 | @end
154 |
155 | /** The @p TGLARView presents 2D view-based overlays and 3D shape overlays on top of a camera preview.
156 | *
157 | * Overlays are virtually attached to their @p -targetPosition. They are positioned in the @p TGLARView
158 | * depending on the current device attitude as if seen through the device's back-facing camera.
159 | *
160 | * @p TGLARView uses @p CoreMotion to get the device orientation in X/Y/Z space, where positive X is north,
161 | * positive Y west and Z points up. If supported by the device the X axis is aligned to magnetic north.
162 | *
163 | * The user may use a horizontal pan gesture to adjust the X/Y plane alignment, e.g. when magnetic north
164 | * is not available on the device. A vertical pan is used to adjust virtual camera height. By default, the
165 | * camera is located at the origin @p (0,0,0).
166 | *
167 | * If video zoom is suppoted for the current video format, a pinch gesture may be used to adjust the
168 | * zoom factor. The virtual camera field of view is adjusted automatically when zooming.
169 | */
170 | @interface TGLARView : UIView
171 |
172 | /// An object conforming to @p TGLARCompass protocol receiving heading updates while the device moves. Default is @p nil.
173 | @property (nonatomic, weak, nullable) IBOutlet id compass;
174 | /// The object that acts as the delegate of this AR view. Default is @p nil.
175 | @property (nonatomic, weak, nullable) IBOutlet id delegate;
176 | /// The object that acts as the data source of this AR view. Default is @p nil.
177 | @property (nonatomic, weak, nullable) IBOutlet id dataSource;
178 |
179 | /// Tells the AR view which device orientation to assume. Controls the selection of the virtual camera's field of view.
180 | @property (nonatomic, assign) UIInterfaceOrientation interfaceOrientation;
181 |
182 | /// Current video zoom factor from @p 1.0 to @p -maxZoomFactor. Default is @p 1.0.
183 | @property (nonatomic, assign) CGFloat zoomFactor;
184 | /// Maximum video zoom factor. @p 1.0 means no zoom.
185 | @property (nonatomic, readonly) CGFloat maxZoomFactor;
186 |
187 | /// Indicates whether CoreMotion can use compass on the current device
188 | @property (nonatomic, readonly, getter=isMagenticNorthAvailable) BOOL magneticNorthAvailable;
189 | /// If compass and location are available use true north heading. Default is @p NO.
190 | @property (nonatomic, assign, getter=isUsingTrueNorth) BOOL useTrueNorth;
191 |
192 | /** Camera height above X/Y plane in meters. Default is @p 0.0.
193 | *
194 | * The user may change this value by a vertical pan gesture.
195 | *
196 | * @sa @p TGLARViewDelegate
197 | */
198 | @property (nonatomic, assign) CGFloat heightOffset;
199 |
200 | /** Camera heading angle in degrees added to device attitude. Default is @p 0.0.
201 | *
202 | * Positive angles result in a clockwise camera rotation.
203 | * The user may change this value by a horizontal pan gesture.
204 | *
205 | * @sa @p TGLARViewDelegate
206 | */
207 | @property (nonatomic, assign) CGFloat headingOffset;
208 |
209 | /** Camera position offset in meters in the X/Y plane.
210 | *
211 | * @param positionOffset.width X axis offset
212 | * @param positionOffset.height Y axis offset
213 | *
214 | * Positive X values move the camera north, negative ones south.
215 | * Positive Y values move the camera west, negative ones east.
216 | */
217 | @property (nonatomic, assign) CGSize positionOffset;
218 |
219 | /// Returns the OpenGL ES context used to draw overlay shapes.
220 | - (nonnull EAGLContext *)renderContext;
221 |
222 | /// Starts the video preview and rendering of the overlays.
223 | - (void)start;
224 | /// Stops the video preview and rendering of the overlays.
225 | - (void)stop;
226 |
227 | /** Reloads the overlays from the current data source.
228 | *
229 | * @sa @p -dataSource
230 | */
231 | - (void)reloadData;
232 |
233 | @end
234 |
--------------------------------------------------------------------------------
/TGLAugmentedRealityView/TGLARView.m:
--------------------------------------------------------------------------------
1 | //
2 | // TGLARView.m
3 | // TGLAugmentedRealityView
4 | //
5 | // Created by Tim Gleue on 09.11.15.
6 | // Copyright (c) 2015 PowerMobile Team (https://powermobile.org)
7 | //
8 | // Permission is hereby granted, free of charge, to any person obtaining a copy
9 | // of this software and associated documentation files (the "Software"), to deal
10 | // in the Software without restriction, including without limitation the rights
11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 | // copies of the Software, and to permit persons to whom the Software is
13 | // furnished to do so, subject to the following conditions:
14 | //
15 | // The above copyright notice and this permission notice shall be included in
16 | // all copies or substantial portions of the Software.
17 | //
18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 | // THE SOFTWARE.
25 |
26 | #import "TGLARView.h"
27 | #import "TGLAROverlay.h"
28 | #import "TGLARViewOverlay.h"
29 | #import "TGLARShapeOverlay.h"
30 | #import "TGLAROverlayContainerView.h"
31 | #import "TGLARCompassView.h"
32 |
33 | #import
34 | #import
35 | #import
36 |
37 | static char FOVARViewKVOContext;
38 |
39 | static const CGFloat kFOVARViewLensAdjustmentFactor = 0.05;
40 |
41 | @interface TGLARView () {
42 |
43 | GLKMatrix4 _deviceTransform;
44 | GLKMatrix4 _cameraTransform;
45 | GLKMatrix4 _userTransformation;
46 |
47 | GLKMatrix4 _viewMatrix;
48 | GLKMatrix4 _projectionMatrix;
49 | }
50 |
51 | @property (nonatomic, strong) CMMotionManager *motionManager;
52 |
53 | @property (nonatomic, strong) UIView *captureView;
54 | @property (nonatomic, strong) AVCaptureDevice *captureDevice;
55 | @property (nonatomic, strong) AVCaptureSession *captureSession;
56 | @property (nonatomic, strong) AVCaptureVideoPreviewLayer *captureLayer;
57 |
58 | @property (nonatomic, strong) GLKView *renderView;
59 | @property (nonatomic, strong) EAGLContext *renderContext;
60 |
61 | @property (nonatomic, strong) TGLAROverlayContainerView *containerView;
62 |
63 | @property (nonatomic, strong) CADisplayLink *displayLink;
64 |
65 | @property (nonatomic, assign) CGFloat fovScalePortrait;
66 | @property (nonatomic, assign) CGFloat fovScaleLandscape;
67 |
68 | @property (nonatomic, assign) CGFloat verticalFovPortrait;
69 | @property (nonatomic, assign) CGFloat verticalFovLandscape;
70 |
71 | @property (nonatomic, strong) NSArray *overlayShapes;
72 |
73 | @property (nonatomic, strong) UITapGestureRecognizer *tapRecognizer;
74 | @property (nonatomic, strong) UIPanGestureRecognizer *panRecognizer;
75 | @property (nonatomic, strong) UIPinchGestureRecognizer *pinchRecognizer;
76 |
77 | @end
78 |
79 | #pragma mark - ARView implementation
80 |
81 | @implementation TGLARView
82 |
83 | @dynamic maxZoomFactor;
84 |
85 | - (instancetype)initWithFrame:(CGRect)frame {
86 |
87 | self = [super initWithFrame:frame];
88 |
89 | if (self) [self initARView];
90 |
91 | return self;
92 | }
93 |
94 | - (instancetype)initWithCoder:(NSCoder *)aDecoder {
95 |
96 | self = [super initWithCoder:aDecoder];
97 |
98 | if (self) [self initARView];
99 |
100 | return self;
101 | }
102 |
103 | - (void)initARView {
104 |
105 | _magneticNorthAvailable = [CMMotionManager availableAttitudeReferenceFrames] & CMAttitudeReferenceFrameXMagneticNorthZVertical;
106 | _useTrueNorth = NO;
107 |
108 | _deviceTransform = GLKMatrix4Identity;
109 | _cameraTransform = GLKMatrix4Identity;
110 | _userTransformation = GLKMatrix4Identity;
111 |
112 | self.fovScalePortrait = 1.0;
113 | self.fovScaleLandscape = 1.0;
114 |
115 | // Make camera preview in background
116 | //
117 | self.captureView = [[UIView alloc] initWithFrame:self.bounds];
118 |
119 | [self addSubview:self.captureView];
120 | [self sendSubviewToBack:self.captureView];
121 |
122 | // Make transparent GL view above preview
123 | //
124 | self.renderContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
125 |
126 | self.renderView = [[GLKView alloc] initWithFrame:self.bounds context:self.renderContext];
127 | self.renderView.drawableColorFormat = GLKViewDrawableColorFormatRGBA8888;
128 | self.renderView.drawableDepthFormat = GLKViewDrawableDepthFormat24;
129 | self.renderView.delegate = self;
130 |
131 | CAEAGLLayer *renderLayer = (CAEAGLLayer *)self.renderView.layer;
132 |
133 | renderLayer.opaque = NO;
134 |
135 | self.renderView.backgroundColor = [UIColor clearColor];
136 | self.renderView.opaque = NO;
137 |
138 | [self insertSubview:self.renderView aboveSubview:self.captureView];
139 |
140 | // Make transparent overlay container above GL view
141 | //
142 | self.containerView = [[TGLAROverlayContainerView alloc] initWithFrame:self.bounds];
143 | self.containerView.backgroundColor = [UIColor clearColor];
144 | self.containerView.opaque = NO;
145 |
146 | [self insertSubview:self.containerView aboveSubview:self.renderView];
147 |
148 | // Add gesture recognizers
149 | //
150 | self.tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapGesture:)];
151 | self.tapRecognizer.delegate = self;
152 |
153 | [self addGestureRecognizer:self.tapRecognizer];
154 |
155 | self.panRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePanGesture:)];
156 | self.panRecognizer.delegate = self;
157 |
158 | [self addGestureRecognizer:self.panRecognizer];
159 |
160 | self.pinchRecognizer = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(handlePinchGesture:)];
161 | self.pinchRecognizer.delegate = self;
162 |
163 | [self addGestureRecognizer:self.pinchRecognizer];
164 | }
165 |
166 | - (void)dealloc {
167 |
168 | [self stop];
169 |
170 | self.overlayShapes = nil;
171 |
172 | [self.captureView removeFromSuperview];
173 | [self.renderView removeFromSuperview];
174 |
175 | if ([EAGLContext currentContext] == self.renderContext) {
176 |
177 | [EAGLContext setCurrentContext:nil];
178 | }
179 |
180 | self.renderContext = nil;
181 | }
182 |
183 | #pragma mark - Layout
184 |
185 | - (void)layoutSubviews {
186 |
187 | [super layoutSubviews];
188 |
189 | [self computeFovFromCameraFormat];
190 | [self updateProjectionMatrix];
191 |
192 | CGRect bounds = self.bounds;
193 |
194 | self.captureView.frame = bounds;
195 | self.captureLayer.frame = bounds;
196 | self.renderView.frame = bounds;
197 | self.containerView.frame = bounds;
198 | }
199 |
200 | #pragma mark - Accessors
201 |
202 | - (void)setInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
203 |
204 | _interfaceOrientation = interfaceOrientation;
205 |
206 | switch (self.interfaceOrientation) {
207 |
208 | case UIInterfaceOrientationPortrait:
209 |
210 | _deviceTransform = GLKMatrix4Identity;
211 | if (self.captureLayer.connection.isVideoOrientationSupported) [self.captureLayer.connection setVideoOrientation:AVCaptureVideoOrientationPortrait];
212 | break;
213 |
214 | case UIInterfaceOrientationPortraitUpsideDown:
215 |
216 | _deviceTransform = GLKMatrix4MakeRotation(GLKMathDegreesToRadians(180.0), 0, 0, 1);
217 | if (self.captureLayer.connection.isVideoOrientationSupported) [self.captureLayer.connection setVideoOrientation:AVCaptureVideoOrientationPortraitUpsideDown];
218 | break;
219 |
220 | case UIInterfaceOrientationLandscapeLeft:
221 |
222 | _deviceTransform = GLKMatrix4MakeRotation(GLKMathDegreesToRadians(-90.0), 0, 0, 1);
223 | if (self.captureLayer.connection.isVideoOrientationSupported) [self.captureLayer.connection setVideoOrientation:AVCaptureVideoOrientationLandscapeLeft];
224 | break;
225 |
226 | case UIInterfaceOrientationLandscapeRight:
227 |
228 | _deviceTransform = GLKMatrix4MakeRotation(GLKMathDegreesToRadians(+90.0), 0, 0, 1);
229 | if (self.captureLayer.connection.isVideoOrientationSupported) [self.captureLayer.connection setVideoOrientation:AVCaptureVideoOrientationLandscapeRight];
230 | break;
231 |
232 | default:
233 |
234 | break;
235 | }
236 |
237 | [self updateProjectionMatrix];
238 | }
239 |
240 | - (void)setZoomFactor:(CGFloat)zoomFactor {
241 |
242 | if (self.captureDevice) {
243 |
244 | NSError *error = nil;
245 |
246 | if ([self.captureDevice lockForConfiguration:&error]) {
247 |
248 | self.captureDevice.videoZoomFactor = MAX(1.0, MIN(zoomFactor, self.maxZoomFactor));
249 |
250 | [self.captureDevice unlockForConfiguration];
251 |
252 | } else {
253 |
254 | NSLog(@"%s ERROR: %@", __PRETTY_FUNCTION__, error.localizedDescription);
255 | }
256 |
257 | _zoomFactor = self.captureDevice.videoZoomFactor;
258 |
259 | } else {
260 |
261 | _zoomFactor = zoomFactor;
262 | }
263 |
264 | [self computeFovFromCameraFormat];
265 | [self updateProjectionMatrix];
266 | }
267 |
268 | - (CGFloat)maxZoomFactor {
269 |
270 | return self.captureDevice.activeFormat.videoMaxZoomFactor;
271 | }
272 |
273 | - (void)setUseTrueNorth:(BOOL)useTrueNorth {
274 |
275 | if (useTrueNorth != _useTrueNorth && self.isMagenticNorthAvailable) {
276 |
277 | if (useTrueNorth && [CMMotionManager availableAttitudeReferenceFrames] & CMAttitudeReferenceFrameXTrueNorthZVertical) {
278 |
279 | _useTrueNorth = YES;
280 |
281 | [self restartDeviceMotionIfNecessary];
282 |
283 | } else if (!useTrueNorth) {
284 |
285 | _useTrueNorth = NO;
286 |
287 | [self restartDeviceMotionIfNecessary];
288 | }
289 | }
290 | }
291 |
292 | - (void)setHeightOffset:(CGFloat)offset {
293 |
294 | if (offset != _heightOffset) {
295 |
296 | _heightOffset = offset;
297 |
298 | [self updateUserTransformation];
299 | }
300 | }
301 |
302 | - (void)setHeadingOffset:(CGFloat)offset {
303 |
304 | if (offset != _headingOffset) {
305 |
306 | _headingOffset = offset;
307 |
308 | [self updateUserTransformation];
309 | }
310 | }
311 |
312 | - (void)setPositionOffset:(CGSize)offset {
313 |
314 | if (!CGSizeEqualToSize(offset, _positionOffset)) {
315 |
316 | _positionOffset = offset;
317 |
318 | [self updateUserTransformation];
319 | }
320 | }
321 |
322 | #pragma mark - Actions
323 |
324 | - (IBAction)handleTapGesture:(UITapGestureRecognizer *)recognizer {
325 |
326 | CGPoint containerTap = [recognizer locationInView:self.containerView];
327 |
328 | UIView *view = [self.containerView hitTest:containerTap withEvent:nil];
329 |
330 | if (view && view != self.containerView) {
331 |
332 | if ([self.delegate respondsToSelector:@selector(arView:didTapViewOverlay:)]) {
333 |
334 | [self.delegate arView:self didTapViewOverlay:(TGLARViewOverlay *)view];
335 | }
336 |
337 | } else {
338 |
339 | CGPoint renderTap = [recognizer locationInView:self.renderView];
340 | TGLARShapeOverlay *shape = [self findShapeAtPoint:renderTap];
341 |
342 | if (shape && [self.delegate respondsToSelector:@selector(arView:didTapShapeOverlay:)]) {
343 |
344 | [self.delegate arView:self didTapShapeOverlay:shape];
345 | }
346 | }
347 | }
348 |
349 | #define PAN_UNLOCKED 0
350 | #define PAN_LOCK_H 1
351 | #define PAN_LOCK_V 2
352 |
353 | - (void)handlePanGesture:(UIPanGestureRecognizer *)recognizer {
354 |
355 | static CGPoint start;
356 | static short lock;
357 | static float heightOffset;
358 | static float headingOffset;
359 |
360 | if (recognizer.state == UIGestureRecognizerStateBegan) {
361 |
362 | start = [recognizer locationInView:self];
363 | lock = PAN_UNLOCKED;
364 | heightOffset = self.heightOffset;
365 | headingOffset = self.headingOffset;
366 |
367 | if ([self.delegate respondsToSelector:@selector(arViewWillBeginUpdatingOffsets:)]) {
368 |
369 | [self.delegate arViewWillBeginUpdatingOffsets:self];
370 | }
371 |
372 | } else if (recognizer.state == UIGestureRecognizerStateChanged) {
373 |
374 | CGPoint touch = [recognizer locationInView:self];
375 |
376 | float deltaX = (touch.x - start.x);
377 | float deltaY = (touch.y - start.y);
378 |
379 | if (lock == PAN_UNLOCKED) {
380 |
381 | lock = (fabs(deltaX) > fabs(deltaY)) ? PAN_LOCK_H : PAN_LOCK_V;
382 | }
383 |
384 | if (lock == PAN_LOCK_H) {
385 |
386 | float deltaHeading = deltaX / self.bounds.size.width;
387 |
388 | self.headingOffset = headingOffset - deltaHeading * [self effectiveHorizontalFov];
389 |
390 | [self updateUserTransformation];
391 |
392 | } else if (lock == PAN_LOCK_V) {
393 |
394 | float deltaHeight = 0.01 * deltaY;
395 | float newHeightOffset = heightOffset + deltaHeight;
396 |
397 | self.heightOffset = newHeightOffset;
398 |
399 | [self updateUserTransformation];
400 | }
401 |
402 | if ([self.delegate respondsToSelector:@selector(arViewDidUpdateOffsets:)]) {
403 |
404 | [self.delegate arViewDidUpdateOffsets:self];
405 | }
406 |
407 | } else if (recognizer.state == UIGestureRecognizerStateCancelled || recognizer.state == UIGestureRecognizerStateEnded) {
408 |
409 | if ([self.delegate respondsToSelector:@selector(arViewDidEndUpdatingOffsets:)]) {
410 |
411 | [self.delegate arViewDidEndUpdatingOffsets:self];
412 | }
413 | }
414 | }
415 |
416 | - (void)handlePinchGesture:(UIPinchGestureRecognizer *)recognizer {
417 |
418 | static CGFloat start;
419 |
420 | if (recognizer.state == UIGestureRecognizerStateBegan) {
421 |
422 | start = self.zoomFactor;
423 |
424 | if ([self.delegate respondsToSelector:@selector(arViewWillBeginZooming:)]) {
425 |
426 | [self.delegate arViewWillBeginZooming:self];
427 | }
428 |
429 | } else if (recognizer.state == UIGestureRecognizerStateChanged) {
430 |
431 | self.zoomFactor = start * recognizer.scale;
432 |
433 | if ([self.delegate respondsToSelector:@selector(arViewDidZoom:)]) {
434 |
435 | [self.delegate arViewDidZoom:self];
436 | }
437 |
438 | } else if (recognizer.state == UIGestureRecognizerStateCancelled || recognizer.state == UIGestureRecognizerStateEnded) {
439 |
440 | if ([self.delegate respondsToSelector:@selector(arViewDidEndZooming:atFactor:)]) {
441 |
442 | [self.delegate arViewDidEndZooming:self atFactor:self.zoomFactor];
443 | }
444 | }
445 | }
446 |
447 | #pragma mark - Methods
448 |
449 | - (void)start {
450 |
451 | [self startCameraPreview];
452 | [self startDeviceMotion];
453 | [self startDisplayLink];
454 |
455 | [self setNeedsLayout];
456 | }
457 |
458 | - (void)stop {
459 |
460 | [self stopDisplayLink];
461 | [self stopDeviceMotion];
462 | [self stopCameraPreview];
463 | }
464 |
465 | - (void)reloadData {
466 |
467 | NSMutableArray *overlayViews = [NSMutableArray array];
468 | NSMutableArray *overlayShapes = [NSMutableArray array];
469 |
470 | NSInteger count = [self.dataSource numberOfOverlaysInARView:self];
471 |
472 | for (NSInteger index = 0; index < count; index++) {
473 |
474 | id overlay = [self.dataSource arView:self overlayAtIndex:index];
475 |
476 | if ([overlay respondsToSelector:@selector(overlayView)]) {
477 |
478 | TGLARViewOverlay *view = overlay.overlayView;
479 |
480 | if (view) [overlayViews addObject:view];
481 | }
482 |
483 | if ([overlay respondsToSelector:@selector(overlayShape)]) {
484 |
485 | TGLARShapeOverlay *shape = overlay.overlayShape;
486 |
487 | if (shape) [overlayShapes addObject:shape];
488 | }
489 | }
490 |
491 | self.overlayShapes = overlayShapes;
492 | self.containerView.overlayViews = overlayViews;
493 | }
494 |
495 | #pragma mark - Camera handling
496 |
497 | - (void)startCameraPreview {
498 |
499 | AVCaptureDevice *camera = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
500 |
501 | if (camera == nil) return;
502 |
503 | self.captureDevice = camera;
504 |
505 | // Apply zoom factor to new camera
506 | //
507 | self.zoomFactor = self.zoomFactor;
508 |
509 | // Register for focus changes to adjust field of view
510 | //
511 | [self.captureDevice addObserver:self forKeyPath:@"lensPosition" options:NSKeyValueObservingOptionInitial context:&FOVARViewKVOContext];
512 |
513 | self.captureSession = [[AVCaptureSession alloc] init];
514 | self.captureSession.sessionPreset = AVCaptureSessionPresetHigh;
515 |
516 | AVCaptureDeviceInput *newVideoInput = [[AVCaptureDeviceInput alloc] initWithDevice:self.captureDevice error:nil];
517 |
518 | [self.captureSession addInput:newVideoInput];
519 |
520 | self.captureLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:self.captureSession];
521 | self.captureLayer.frame = self.captureView.bounds;
522 | self.captureLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
523 |
524 | [self.captureView.layer addSublayer:self.captureLayer];
525 |
526 | // Start the session.
527 | //
528 | // This is done asychronously since -startRunning
529 | // doesn't return until the session is running.
530 | //
531 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
532 |
533 | [self.captureSession startRunning];
534 | });
535 | }
536 |
537 | - (void)stopCameraPreview {
538 |
539 | [self.captureSession stopRunning];
540 | [self.captureLayer removeFromSuperlayer];
541 |
542 | self.captureLayer = nil;
543 | self.captureSession = nil;
544 |
545 | [self.captureDevice removeObserver:self forKeyPath:@"lensPosition"];
546 |
547 | self.captureDevice = nil;
548 | }
549 |
550 | #pragma mark - Motion handling
551 |
552 | - (void)startDeviceMotion {
553 |
554 | self.motionManager = [[CMMotionManager alloc] init];
555 | self.motionManager.showsDeviceMovementDisplay = YES;
556 | self.motionManager.deviceMotionUpdateInterval = 1.0 / 60.0;
557 |
558 | // When available use compass to have x axis pointing to magnetic north
559 | //
560 | CMAttitudeReferenceFrame referenceFrame;
561 |
562 | if (self.isMagenticNorthAvailable) {
563 |
564 | referenceFrame = self.isUsingTrueNorth ? CMAttitudeReferenceFrameXTrueNorthZVertical : CMAttitudeReferenceFrameXMagneticNorthZVertical;
565 |
566 | } else {
567 |
568 | referenceFrame = CMAttitudeReferenceFrameXArbitraryZVertical;
569 | }
570 |
571 | [self.motionManager startDeviceMotionUpdatesUsingReferenceFrame:referenceFrame];
572 | }
573 |
574 | - (void)stopDeviceMotion {
575 |
576 | [self.motionManager stopDeviceMotionUpdates];
577 |
578 | self.motionManager = nil;
579 | }
580 |
581 | - (void)restartDeviceMotionIfNecessary {
582 |
583 | if (self.motionManager) {
584 |
585 | [self stopDeviceMotion];
586 | [self startDeviceMotion];
587 | }
588 | }
589 |
590 | #pragma mark - Redraw handling
591 |
592 | - (void)startDisplayLink {
593 |
594 | self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(onDisplayLink:)];
595 |
596 | [self.displayLink setFrameInterval:1];
597 | [self.displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
598 | }
599 |
600 | - (void)stopDisplayLink {
601 |
602 | [self.displayLink invalidate];
603 |
604 | self.displayLink = nil;
605 | }
606 |
607 | - (void)onDisplayLink:(id)sender {
608 |
609 | CMDeviceMotion *d = self.motionManager.deviceMotion;
610 |
611 | if (d != nil) {
612 |
613 | CMRotationMatrix r = d.attitude.rotationMatrix;
614 |
615 | _cameraTransform = GLKMatrix4Make(r.m11, r.m21, r.m31, 0.0, r.m12, r.m22, r.m32, 0.0, r.m13, r.m23, r.m33, 0.0, 0.0, 0.0, 0.0, 1.0);
616 | }
617 |
618 | // Trigger -glkView:drawInRect:
619 | //
620 | [self.renderView setNeedsDisplay];
621 | }
622 |
623 | - (void)glkView:(GLKView *)view drawInRect:(CGRect)rect {
624 |
625 | // Compute modelview and projection matrices
626 | // and use them to transform GL overlay shapes
627 | // as well as overlay views and compass
628 | //
629 | GLKMatrix4 cameraMatrix = GLKMatrix4Multiply(_cameraTransform, _userTransformation);
630 |
631 | _viewMatrix = GLKMatrix4Multiply(_deviceTransform, cameraMatrix);
632 |
633 | [self drawShapes:NO];
634 |
635 | self.containerView.overlayTransformation = GLKMatrix4Multiply(_projectionMatrix, _viewMatrix);
636 |
637 | if (self.compass) {
638 |
639 | bool inverted;
640 |
641 | GLKMatrix4 inverseView = GLKMatrix4Invert(_viewMatrix, &inverted);
642 |
643 | if (inverted) {
644 |
645 | GLKVector3 xAxis = GLKVector3Make(1, 0, 0);
646 | GLKVector3 northAxis = GLKMatrix4MultiplyVector3(inverseView, xAxis);
647 |
648 | northAxis.z = 0.0;
649 | northAxis = GLKVector3Normalize(northAxis);
650 |
651 | float northDot = GLKVector3DotProduct(northAxis, xAxis);
652 | float northAngle = GLKMathRadiansToDegrees(acosf(northDot));
653 |
654 | if (northAxis.y > 0.0) northAngle = 360.0 - northAngle;
655 |
656 | northAngle -= 90.0;
657 |
658 | if (northAngle < 0.0) northAngle += 360.0;
659 |
660 | [self.compass setHeadingAngle:northAngle];
661 | }
662 | }
663 | }
664 |
665 | - (void)drawShapes:(BOOL)picking {
666 |
667 | glEnable(GL_DEPTH_TEST);
668 | glEnable(GL_CULL_FACE);
669 |
670 | if (picking) {
671 |
672 | glClearColor(1.0f, 1.0f, 1.0f, picking ? 1.0f : 0.0f);
673 |
674 | } else {
675 |
676 | glClearColor(0.0f, 0.0f, 0.0f, picking ? 1.0f : 0.0f);
677 | }
678 |
679 | glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
680 |
681 | for (NSInteger idx = 0; idx < self.overlayShapes.count; idx++) {
682 |
683 | TGLARShapeOverlay *shape = self.overlayShapes[idx];
684 |
685 | shape.viewMatrix = _viewMatrix;
686 | shape.projectionMatrix = _projectionMatrix;
687 |
688 | if (picking) {
689 |
690 | // TODO: idx > 254
691 | //
692 | [shape drawUsingConstantColor:GLKVector4Make((idx + 1) / 255.0f, 0.0f, 0.0f, 0.0f)];
693 |
694 | } else {
695 |
696 | [shape draw];
697 | }
698 | }
699 | }
700 |
701 | #pragma mark - Pick handling
702 |
703 | // See http://stackoverflow.com/a/10784181
704 |
705 | - (TGLARShapeOverlay *)findShapeAtPoint:(CGPoint)point {
706 |
707 | [EAGLContext setCurrentContext:self.renderContext];
708 |
709 | NSInteger height = self.renderView.drawableHeight;
710 | NSInteger width = self.renderView.drawableWidth;
711 |
712 | GLuint framebuffer;
713 |
714 | glGenFramebuffers(1, &framebuffer);
715 | glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
716 |
717 | GLuint colorRenderbuffer;
718 |
719 | glGenRenderbuffers(1, &colorRenderbuffer);
720 | glBindRenderbuffer(GL_RENDERBUFFER, colorRenderbuffer);
721 |
722 | glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8_OES, (GLsizei)width, (GLsizei)height);
723 | glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorRenderbuffer);
724 |
725 | GLuint depthRenderbuffer;
726 |
727 | glGenRenderbuffers(1, &depthRenderbuffer);
728 | glBindRenderbuffer(GL_RENDERBUFFER, depthRenderbuffer);
729 |
730 | glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, (GLsizei)width, (GLsizei)height);
731 | glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthRenderbuffer);
732 |
733 | GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
734 |
735 | if (status != GL_FRAMEBUFFER_COMPLETE) {
736 |
737 | NSLog(@"Framebuffer status: %x", (int)status);
738 | return nil;
739 | }
740 |
741 | [self drawShapes:YES];
742 |
743 | Byte pixelColor[4] = {0,};
744 | CGFloat scale = UIScreen.mainScreen.scale;
745 |
746 | glReadPixels(point.x * scale, (height - (point.y * scale)), 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixelColor);
747 |
748 | //NSLog(@"%s Pixel color @ %@: %x %x %x %x", __PRETTY_FUNCTION__, NSStringFromCGPoint(point), pixelColor[0], pixelColor[1], pixelColor[2], pixelColor[3]);
749 |
750 | glDeleteRenderbuffers(1, &depthRenderbuffer);
751 | glDeleteRenderbuffers(1, &colorRenderbuffer);
752 | glDeleteFramebuffers(1, &framebuffer);
753 |
754 | [self.renderView bindDrawable];
755 |
756 | NSInteger idx = pixelColor[0];
757 |
758 | return (idx > 0 && idx <= self.overlayShapes.count) ? self.overlayShapes[idx-1] : nil;
759 | }
760 |
761 | #pragma mark - Projection matrix handling
762 |
763 | - (void)computeFovFromCameraFormat {
764 |
765 | if (self.captureDevice) {
766 |
767 | CGFloat aspectRatio = self.bounds.size.width / self.bounds.size.height;
768 |
769 | if (aspectRatio > 1.0) aspectRatio = 1.0 / aspectRatio;
770 |
771 | AVCaptureDeviceFormat *activeFormat = self.captureDevice.activeFormat;
772 | CGFloat activeFOV = GLKMathRadiansToDegrees(2.0 * atan(tan(0.5 * GLKMathDegreesToRadians(activeFormat.videoFieldOfView)) / self.captureDevice.videoZoomFactor));
773 |
774 | CMFormatDescriptionRef description = activeFormat.formatDescription;
775 | CMVideoDimensions dimensions = CMVideoFormatDescriptionGetDimensions(description);
776 |
777 | CGFloat aspectWidth = (CGFloat)dimensions.height / aspectRatio;
778 | CGFloat aspectHeight = (CGFloat)dimensions.width * aspectRatio;
779 |
780 | CGFloat aspectFOV;
781 |
782 | if (aspectWidth < dimensions.width) {
783 |
784 | aspectFOV = GLKMathRadiansToDegrees(2.0 * atan(aspectWidth / (CGFloat)dimensions.width * tan(0.5 * GLKMathDegreesToRadians(activeFOV))));
785 |
786 | } else if (aspectHeight < dimensions.height) {
787 |
788 | aspectFOV = activeFOV;
789 |
790 | } else {
791 |
792 | aspectFOV = activeFOV;
793 | }
794 |
795 | self.verticalFovPortrait = aspectFOV;
796 | self.verticalFovLandscape = GLKMathRadiansToDegrees(2.0 * atan(tan(0.5 * GLKMathDegreesToRadians(aspectFOV)) * aspectRatio));
797 |
798 | if ([self.compass respondsToSelector:@selector(setFieldOfView:)]) {
799 |
800 | [self.compass setFieldOfView:self.effectiveHorizontalFov];
801 | }
802 | }
803 | }
804 |
805 | - (void)updateProjectionMatrix {
806 |
807 | // Initialize camera & projection matrix
808 | //
809 | CGFloat fovy = self.effectiveVerticalFov;
810 | CGFloat aspect = self.bounds.size.width / self.bounds.size.height;
811 |
812 | CGFloat near = ([self.delegate respondsToSelector:@selector(arViewShapeOverlayNearClippingDistance:)]) ? [self.delegate arViewShapeOverlayNearClippingDistance:self] : 1.0;
813 | CGFloat far = ([self.delegate respondsToSelector:@selector(arViewShapeOverlayFarClippingDistance:)]) ? [self.delegate arViewShapeOverlayFarClippingDistance:self] : 10000.0;
814 |
815 | _projectionMatrix = GLKMatrix4MakePerspective(GLKMathDegreesToRadians(fovy), aspect, near, far);
816 | }
817 |
818 | - (void)updateUserTransformation {
819 |
820 | GLKMatrix4 rotation = GLKMatrix4MakeRotation(self.headingOffset / 180.0 * M_PI, 0.0, 0.0, 1.0);
821 | GLKMatrix4 translation = GLKMatrix4MakeTranslation(-self.positionOffset.width, -self.positionOffset.height, -self.heightOffset);
822 |
823 | _userTransformation = GLKMatrix4Multiply(rotation, translation);
824 | }
825 |
826 | #pragma mark - Key-value Observing
827 |
828 | - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
829 |
830 | if (context == &FOVARViewKVOContext && [keyPath isEqualToString:@"lensPosition"]) {
831 |
832 | CGFloat scale = 1.0 + self.captureDevice.lensPosition * kFOVARViewLensAdjustmentFactor;
833 |
834 | self.fovScalePortrait = self.fovScaleLandscape = scale;
835 |
836 | } else if ([super respondsToSelector:@selector(observeValueForKeyPath:ofObject:change:context:)]) {
837 |
838 | [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
839 | }
840 | }
841 |
842 | #pragma mark - Helpers
843 |
844 | - (CGFloat)effectiveVerticalFov {
845 |
846 | if (UIInterfaceOrientationIsPortrait(self.interfaceOrientation)) {
847 |
848 | return self.fovScalePortrait * self.verticalFovPortrait;
849 |
850 | } else {
851 |
852 | return self.fovScaleLandscape * self.verticalFovLandscape;
853 | }
854 | }
855 |
856 | - (CGFloat)effectiveHorizontalFov {
857 |
858 | if (UIInterfaceOrientationIsPortrait(self.interfaceOrientation)) {
859 |
860 | return self.fovScalePortrait * self.verticalFovLandscape;
861 |
862 | } else {
863 |
864 | return self.fovScaleLandscape * self.verticalFovPortrait;
865 | }
866 | }
867 |
868 | @end
869 |
--------------------------------------------------------------------------------
/TGLAugmentedRealityView/TGLARViewOverlay.h:
--------------------------------------------------------------------------------
1 | //
2 | // TGLARViewOverlay.h
3 | // TGLAugmentedRealityView
4 | //
5 | // Created by Tim Gleue on 13.11.15.
6 | // Copyright (c) 2015 PowerMobile Team (https://powermobile.org)
7 | //
8 | // Permission is hereby granted, free of charge, to any person obtaining a copy
9 | // of this software and associated documentation files (the "Software"), to deal
10 | // in the Software without restriction, including without limitation the rights
11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 | // copies of the Software, and to permit persons to whom the Software is
13 | // furnished to do so, subject to the following conditions:
14 | //
15 | // The above copyright notice and this permission notice shall be included in
16 | // all copies or substantial portions of the Software.
17 | //
18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 | // THE SOFTWARE.
25 |
26 | #import
27 |
28 | #import "TGLAROverlay.h"
29 |
30 | /** A @p UIView subclass to present 2D content in a @p TGLARView.
31 | *
32 | * Any subviews to this overlay @a must be placed inside @p -contentView.
33 | *
34 | * The overlay draws a callout line from the overlay's @p -targetPosition
35 | * and places the content view at the other end of the callout line.
36 | *
37 | * The content view background is transparent by default and is @a not
38 | * the same as the @p -calloutColor.
39 | */
40 | @interface TGLARViewOverlay : UIView
41 |
42 | /// The overlay this view belongs to.
43 | @property (nonatomic, weak, nullable) id overlay;
44 |
45 | /// The view containing the all of the overlays content-related views.
46 | @property (nonatomic, readonly, nonnull) UIView *contentView;
47 |
48 | /** If set to @YES place the callout and the @p -contentView below the
49 | * overlay @-targetPosition, above otherwise. Default is @p NO. */
50 | @property (nonatomic, assign) BOOL upsideDown;
51 | /** If set to @YES place the callout and the @p -contentView to the
52 | * left of @-targetPosition, to the right otherwise. Default is @p NO. */
53 | @property (nonatomic, assign) BOOL rightAligned;
54 |
55 | /** The vertical distance in points between the overlay @p -targetPosition
56 | * on screen and the top/bottom edge of the @p -contentView. Default is @p 100.0. */
57 | @property (nonatomic, assign) CGFloat calloutLength;
58 | /// The callout line width in points. Default is 2.0.
59 | @property (nonatomic, assign) CGFloat calloutLineWidth;
60 | /// The callout line color. Default is @p [UIColor whiteColor].
61 | @property (nonatomic, copy, nullable) UIColor *calloutLineColor;
62 |
63 | /// Private property. For internal use only
64 | @property (nonatomic, assign) GLKVector3 viewPosition;
65 |
66 | @end
67 |
--------------------------------------------------------------------------------
/TGLAugmentedRealityView/TGLARViewOverlay.m:
--------------------------------------------------------------------------------
1 | //
2 | // TGLARViewOverlay.m
3 | // TGLAugmentedRealityView
4 | //
5 | // Created by Tim Gleue on 13.11.15.
6 | // Copyright (c) 2015 PowerMobile Team (https://powermobile.org)
7 | //
8 | // Permission is hereby granted, free of charge, to any person obtaining a copy
9 | // of this software and associated documentation files (the "Software"), to deal
10 | // in the Software without restriction, including without limitation the rights
11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 | // copies of the Software, and to permit persons to whom the Software is
13 | // furnished to do so, subject to the following conditions:
14 | //
15 | // The above copyright notice and this permission notice shall be included in
16 | // all copies or substantial portions of the Software.
17 | //
18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 | // THE SOFTWARE.
25 |
26 | #import "TGLARViewOverlay.h"
27 |
28 | @implementation TGLARViewOverlay
29 |
30 | - (instancetype)initWithFrame:(CGRect)frame {
31 |
32 | self = [super initWithFrame:frame];
33 |
34 | if (self) [self initOverlay];
35 |
36 | return self;
37 | }
38 |
39 | - (instancetype)initWithCoder:(NSCoder *)aDecoder {
40 |
41 | self = [super initWithCoder:aDecoder];
42 |
43 | if (self) [self initOverlay];
44 |
45 | return self;
46 | }
47 |
48 | - (void)initOverlay {
49 |
50 | self.backgroundColor = [UIColor clearColor];
51 | self.opaque = NO;
52 |
53 | self.layer.shadowColor = [UIColor blackColor].CGColor;
54 | self.layer.shadowOffset = CGSizeMake(0.0, 0.0);
55 | self.layer.shadowOpacity = 0.5;
56 | self.layer.shadowRadius = 3.0;
57 |
58 | _contentView = [[UIView alloc] init];
59 | _contentView.backgroundColor = [UIColor clearColor];
60 | _contentView.opaque = NO;
61 | _contentView.translatesAutoresizingMaskIntoConstraints = NO;
62 |
63 | [self addSubview:_contentView];
64 |
65 | _calloutLineWidth = 2.0;
66 | _calloutLineColor = [UIColor whiteColor];
67 |
68 | _calloutLength = 100.0;
69 | }
70 |
71 | #pragma mark - Accessors
72 |
73 | - (void)setUpsideDown:(BOOL)upsideDown {
74 |
75 | _upsideDown = upsideDown;
76 |
77 | [self setNeedsLayout];
78 | }
79 |
80 | - (void)setRightAligned:(BOOL)rightAligned {
81 |
82 | _rightAligned = rightAligned;
83 |
84 | [self setNeedsLayout];
85 | }
86 |
87 | - (void)setCalloutLength:(CGFloat)calloutLength {
88 |
89 | _calloutLength = calloutLength;
90 |
91 | [self setNeedsLayout];
92 | }
93 |
94 | - (void)setCalloutLineWidth:(CGFloat)calloutLineWidth {
95 |
96 | _calloutLineWidth = calloutLineWidth;
97 |
98 | [self setNeedsDisplay];
99 | }
100 |
101 | - (void)setCalloutLineColor:(UIColor *)calloutLineColor {
102 |
103 | _calloutLineColor = [calloutLineColor copy];
104 |
105 | [self setNeedsDisplay];
106 | }
107 |
108 | #pragma mark - Layout
109 |
110 | - (CGSize)sizeThatFits:(CGSize)size {
111 |
112 | CGSize contentSize = [self.contentView sizeThatFits:size];
113 |
114 | contentSize.height = MAX(self.calloutLength, contentSize.height);
115 |
116 | return contentSize;
117 | }
118 |
119 | - (void)layoutSubviews {
120 |
121 | [super layoutSubviews];
122 |
123 | CGRect frame = self.contentView.frame;
124 |
125 | frame.origin.x = 0.0;
126 | frame.origin.y = self.upsideDown ? CGRectGetHeight(self.bounds) - CGRectGetHeight(frame) : 0.0;
127 |
128 | self.contentView.frame = frame;
129 | }
130 |
131 | #pragma mark - Interaction
132 |
133 | - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
134 |
135 | CGPoint contentPoint = [self.contentView convertPoint:point fromView:self];
136 |
137 | if ([self.contentView pointInside:contentPoint withEvent:event] && self.isUserInteractionEnabled && !self.isHidden && self.alpha > 0.01) {
138 |
139 | for (UIView *subview in [self.contentView.subviews reverseObjectEnumerator]) {
140 |
141 | CGPoint subviewPoint = [subview convertPoint:point fromView:self];
142 | UIView *hitView = [subview hitTest:subviewPoint withEvent:event];
143 |
144 | if (hitView) return hitView;
145 | }
146 |
147 | return self;
148 | }
149 |
150 | return nil;
151 | }
152 |
153 | #pragma mark - Drawing
154 |
155 | - (void)drawRect:(CGRect)rect {
156 |
157 | CGFloat x = self.rightAligned ? CGRectGetWidth(self.bounds) : 0.0;
158 |
159 | CGPoint originPoint = CGPointMake(x, 0.0);
160 | CGPoint targetPoint = CGPointMake(x, CGRectGetHeight(self.bounds));
161 |
162 | [self.calloutLineColor setStroke];
163 |
164 | UIBezierPath *calloutLine = [UIBezierPath bezierPath];
165 |
166 | [calloutLine moveToPoint:originPoint];
167 | [calloutLine addLineToPoint:targetPoint];
168 |
169 | calloutLine.lineWidth = self.calloutLineWidth;
170 | [calloutLine stroke];
171 | }
172 |
173 | @end
174 |
--------------------------------------------------------------------------------