├── .gitignore ├── Assets ├── Calibration.cs ├── Calibration.cs.meta ├── CalibrationExample.unity ├── CalibrationExample.unity.meta ├── CorrespondenceAcquisition.cs ├── CorrespondenceAcquisition.cs.meta ├── DrawLine.cs ├── DrawLine.cs.meta ├── Plugins.meta ├── Plugins │ ├── x86.meta │ └── x86 │ │ ├── OpenCvSharp.CPlusPlus.dll │ │ ├── OpenCvSharp.CPlusPlus.dll.config │ │ ├── OpenCvSharp.CPlusPlus.dll.config.meta │ │ ├── OpenCvSharp.CPlusPlus.dll.meta │ │ ├── OpenCvSharp.dll │ │ ├── OpenCvSharp.dll.config │ │ ├── OpenCvSharp.dll.config.meta │ │ ├── OpenCvSharp.dll.meta │ │ ├── OpenCvSharpExtern.dll │ │ └── OpenCvSharpExtern.dll.meta ├── PointsRecordReplay.cs ├── PointsRecordReplay.cs.meta ├── ProjectionController.cs ├── ProjectionController.cs.meta ├── Recording.cs ├── Recording.cs.meta ├── Rotation.cs ├── Rotation.cs.meta ├── RotationConversion.cs ├── RotationConversion.cs.meta ├── SerializableVector3.cs ├── SerializableVector3.cs.meta ├── SerializeExtensions.cs ├── SerializeExtensions.cs.meta ├── crosshair.png └── crosshair.png.meta ├── OpenCVDlls ├── opencv_calib3d2410.dll ├── opencv_core2410.dll ├── opencv_features2d2410.dll ├── opencv_flann2410.dll └── opencv_imgproc2410.dll ├── ProjectSettings ├── AudioManager.asset ├── DynamicsManager.asset ├── EditorBuildSettings.asset ├── EditorSettings.asset ├── GraphicsSettings.asset ├── InputManager.asset ├── NavMeshLayers.asset ├── NetworkManager.asset ├── Physics2DSettings.asset ├── ProjectSettings.asset ├── QualitySettings.asset ├── TagManager.asset └── TimeManager.asset ├── README.md └── UnityProjectionMapping.unitypackage /.gitignore: -------------------------------------------------------------------------------- 1 | # =============== # 2 | # Unity generated # 3 | # =============== # 4 | Temp/ 5 | Library/ 6 | 7 | # ===================================== # 8 | # Visual Studio / MonoDevelop generated # 9 | # ===================================== # 10 | ExportedObj/ 11 | obj/ 12 | *.svd 13 | *.userprefs 14 | /*.csproj 15 | *.pidb 16 | *.suo 17 | /*.sln 18 | *.user 19 | *.unityproj 20 | *.booproj 21 | 22 | # ============ # 23 | # OS generated # 24 | # ============ # 25 | .DS_Store 26 | .DS_Store? 27 | ._* 28 | .Spotlight-V100 29 | .Trashes 30 | ehthumbs.db 31 | Thumbs.db -------------------------------------------------------------------------------- /Assets/Calibration.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | using OpenCvSharp; 4 | using OpenCvSharp.CPlusPlus; 5 | using System.Collections.Generic; 6 | using System.Text; 7 | using System.IO; 8 | using Assets; 9 | 10 | public class Calibration { 11 | // NB some code adapted from OpenCVSharp camera calibration example: 12 | // https://github.com/shimat/opencvsharp/blob/master/sample/CStyleSamplesCS/Samples/CalibrateCamera.cs 13 | 14 | private Camera _mainCamera; 15 | private static int _numImages = 1; 16 | private double height; 17 | private double width; 18 | 19 | public Calibration(Camera mainCamera) 20 | { 21 | _mainCamera = mainCamera; 22 | height = (double)Screen.height; 23 | width = (double)Screen.width; 24 | } 25 | 26 | public double calibrateFromCorrespondences(List imagePoints, List worldPoints) 27 | { 28 | int numPoints = imagePoints.Count; 29 | CvMat pointCounts = CreatePointCountMatrix(numPoints); 30 | CvMat imagePointsCvMat = PutImagePointsIntoCVMat(imagePoints, numPoints); 31 | CvMat worldPointsCvMat = PutObjectPointsIntoCVMat(worldPoints, numPoints, _numImages); 32 | Size size = new Size(width, height); 33 | CvMat distortion = new CvMat(1, 4, MatrixType.F64C1); 34 | CvMat rotation = new CvMat(1, 3, MatrixType.F32C1); 35 | CvMat translation = new CvMat(1, 3, MatrixType.F32C1); 36 | CvMat intrinsic = CreateIntrinsicGuess(height, width); 37 | var flags = CalibrationFlag.ZeroTangentDist | CalibrationFlag.UseIntrinsicGuess | 38 | CalibrationFlag.FixK1 | CalibrationFlag.FixK2 | CalibrationFlag.FixK3 | CalibrationFlag.FixK4 | CalibrationFlag.FixK5 | CalibrationFlag.FixK6; 39 | 40 | Cv.CalibrateCamera2(worldPointsCvMat, imagePointsCvMat, pointCounts, size, intrinsic, distortion, rotation, translation, flags); 41 | 42 | ApplyCalibrationToUnityCamera(intrinsic, rotation, translation); 43 | 44 | return CalculateReprojectionError(imagePoints, numPoints, imagePointsCvMat, worldPointsCvMat, intrinsic, distortion, rotation, translation); 45 | } 46 | 47 | private void ApplyCalibrationToUnityCamera(CvMat intrinsic, CvMat rotation, CvMat translation) 48 | { 49 | CvMat rotationInverse = GetRotationMatrixFromRotationVector(rotation).Transpose(); // transpose is same as inverse for rotation matrix 50 | CvMat transFinal = (rotationInverse * -1) * translation.Transpose(); 51 | _mainCamera.projectionMatrix = LoadProjectionMatrix((float)intrinsic[0, 0], (float)intrinsic[1, 1], (float)intrinsic[0, 2], (float)intrinsic[1, 2]); 52 | ApplyTranslationAndRotationToCamera(transFinal, RotationConversion.RotationMatrixToEulerZXY(rotationInverse)); 53 | } 54 | 55 | private static CvMat CreatePointCountMatrix(int numPoints) 56 | { 57 | int[] pointCountsValue = new int[_numImages]; 58 | pointCountsValue[0] = numPoints; 59 | CvMat pointCounts = new CvMat(_numImages, 1, MatrixType.S32C1, pointCountsValue); 60 | return pointCounts; 61 | } 62 | 63 | private static CvMat GetRotationMatrixFromRotationVector(CvMat rotation_) 64 | { 65 | CvMat rotationFull = new CvMat(3, 3, MatrixType.F32C1); 66 | // get full rotation matrix from rotation vector 67 | Cv.Rodrigues2(rotation_, rotationFull); 68 | float[] LHSflipBackMatrix = new float[] { 1.0f, 0.0f, 0.0f, 69 | 0.0f, 1.0f, 0.0f, 70 | 0.0f, 0.0f, -1.0f }; 71 | CvMat LHSflipBackMatrixM = new CvMat(3, 3, MatrixType.F32C1, LHSflipBackMatrix); 72 | CvMat rotLHS = rotationFull * LHSflipBackMatrixM; // invert Z (as we did before when savings points) to get from RHS -> LHS 73 | return rotLHS; 74 | } 75 | 76 | private double CalculateReprojectionError(List _imagePositions, int pointsCount, CvMat imagePoints, CvMat objectPoints, CvMat intrinsic, CvMat distortion, CvMat rotation_, CvMat translation_) 77 | { 78 | // openCV SSD taken from http://stackoverflow.com/questions/23781089/opencv-calibratecamera-2-reprojection-error-and-custom-computed-one-not-agree 79 | 80 | CvMat imagePointsOut = PutImagePointsIntoCVMat(_imagePositions, pointsCount); // will be overwritten, but should be correct size. Hacky and slow imp 81 | Cv.ProjectPoints2(objectPoints, rotation_, translation_, intrinsic, distortion, imagePointsOut); 82 | 83 | var ar1 = imagePoints.ToArray(); 84 | var ar2 = imagePointsOut.ToArray(); 85 | double diff = 0.0f; 86 | for (int i = 0; i < pointsCount; i++) 87 | { 88 | diff = diff + System.Math.Pow(System.Math.Abs(ar1[i].Val0 - ar2[i].Val0), 2.0) + System.Math.Pow(System.Math.Abs(ar1[i].Val1 - ar2[i].Val1), 2.0); 89 | } 90 | diff = System.Math.Sqrt(diff / pointsCount); 91 | return diff; 92 | } 93 | 94 | private Matrix4x4 LoadProjectionMatrix(float fx, float fy, float cx, float cy) 95 | { 96 | // https://github.com/kylemcdonald/ofxCv/blob/88620c51198fc3992fdfb5c0404c37da5855e1e1/libs/ofxCv/src/Calibration.cpp 97 | float w = _mainCamera.pixelWidth; 98 | float h = _mainCamera.pixelHeight; 99 | float nearDist = _mainCamera.nearClipPlane; 100 | float farDist = _mainCamera.farClipPlane; 101 | 102 | return MakeFrustumMatrix( 103 | nearDist * (-cx) / fx, nearDist * (w - cx) / fx, 104 | nearDist * (cy) / fy, nearDist * (cy - h) / fy, 105 | nearDist, farDist); 106 | } 107 | 108 | private Matrix4x4 MakeFrustumMatrix(float left, float right, 109 | float bottom, float top, 110 | float zNear, float zFar) 111 | { 112 | // https://github.com/openframeworks/openFrameworks/blob/master/libs/openFrameworks/math/ofMatrix4x4.cpp 113 | // note transpose of ofMatrix4x4 wr.t OpenGL documentation, since the OSG use post multiplication rather than pre. 114 | // NB this has been transposed here from the original openframeworks code 115 | 116 | float A = (right + left) / (right - left); 117 | float B = (top + bottom) / (top - bottom); 118 | float C = -(zFar + zNear) / (zFar - zNear); 119 | float D = -2.0f * zFar * zNear / (zFar - zNear); 120 | 121 | var persp = new Matrix4x4(); 122 | persp[0, 0] = 2.0f * zNear / (right - left); 123 | persp[1, 1] = 2.0f * zNear / (top - bottom); 124 | persp[2, 0] = A; 125 | persp[2, 1] = B; 126 | persp[2, 2] = C; 127 | persp[2, 3] = -1.0f; 128 | persp[3, 2] = D; 129 | 130 | var rhsToLhs = new Matrix4x4(); 131 | rhsToLhs[0, 0] = 1.0f; 132 | rhsToLhs[1, 1] = -1.0f; // Flip Y (RHS -> LHS) 133 | rhsToLhs[2, 2] = 1.0f; 134 | rhsToLhs[3, 3] = 1.0f; 135 | 136 | return rhsToLhs * persp.transpose; // see comment above 137 | } 138 | 139 | private void ApplyTranslationAndRotationToCamera(CvMat translation, Rotation r) 140 | { 141 | double tx = translation[0, 0]; 142 | double ty = translation[0, 1]; 143 | double tz = translation[0, 2]; 144 | 145 | _mainCamera.transform.position = new Vector3((float)tx, (float)ty, (float)tz); 146 | _mainCamera.transform.eulerAngles = new Vector3((float)r.X, (float)r.Y, (float)r.Z); 147 | } 148 | 149 | private CvMat CreateIntrinsicGuess(double height, double width) 150 | { 151 | // from https://docs.google.com/spreadsheet/ccc?key=0AuC4NW61c3-cdDFhb1JxWUFIVWpEdXhabFNjdDJLZXc#gid=0 152 | // taken from http://www.neilmendoza.com/projector-field-view-calculator/ 153 | float hfov = 91.2705674249382f; 154 | float vfov = 59.8076333281726f; 155 | 156 | double fx = (double)((float)width / (2.0f * Mathf.Tan(0.5f * hfov * Mathf.Deg2Rad))); 157 | double fy = (double)((float)height / (2.0f * Mathf.Tan(0.5f * vfov * Mathf.Deg2Rad))); 158 | 159 | double cy = height / 2.0; 160 | double cx = width / 2.0; 161 | 162 | double[] intrGuess = new double[] { fx, 0.0, cx, 163 | 0.0, fy, cy, 164 | 0.0, 0.0, 1.0 }; 165 | return new CvMat(3, 3, MatrixType.F64C1, intrGuess); 166 | } 167 | 168 | private CvMat PutObjectPointsIntoCVMat(List _objectPositions, int pointsCount, int ImageNum) 169 | { 170 | CvPoint3D32f[,] objects = new CvPoint3D32f[ImageNum, pointsCount]; 171 | 172 | for (int i = 0; i < pointsCount; i++) 173 | { 174 | objects[0, i] = new CvPoint3D32f 175 | { 176 | X = _objectPositions[i].x, 177 | Y = _objectPositions[i].y, 178 | Z = _objectPositions[i].z * -1 179 | }; 180 | } 181 | 182 | return new CvMat(pointsCount, 3, MatrixType.F32C1, objects); 183 | } 184 | 185 | private CvMat PutImagePointsIntoCVMat(List _imagePositions, int pointsCount) 186 | { 187 | List allCorners = new List(pointsCount); 188 | 189 | for (int i = 0; i < _imagePositions.Count; i++) 190 | { 191 | allCorners.Add(new CvPoint2D32f(_imagePositions[i].x, _imagePositions[i].y)); 192 | } 193 | 194 | return new CvMat(pointsCount, 1, MatrixType.F32C2, allCorners.ToArray()); 195 | } 196 | } -------------------------------------------------------------------------------- /Assets/Calibration.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: a386c084faceb334087527940d7b1f89 3 | MonoImporter: 4 | serializedVersion: 2 5 | defaultReferences: [] 6 | executionOrder: 0 7 | icon: {instanceID: 0} 8 | userData: 9 | -------------------------------------------------------------------------------- /Assets/CalibrationExample.unity: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!29 &1 4 | SceneSettings: 5 | m_ObjectHideFlags: 0 6 | m_PVSData: 7 | m_PVSObjectsArray: [] 8 | m_PVSPortalsArray: [] 9 | m_OcclusionBakeSettings: 10 | smallestOccluder: 5 11 | smallestHole: .25 12 | backfaceThreshold: 100 13 | --- !u!104 &2 14 | RenderSettings: 15 | m_Fog: 0 16 | m_FogColor: {r: .5, g: .5, b: .5, a: 1} 17 | m_FogMode: 3 18 | m_FogDensity: .00999999978 19 | m_LinearFogStart: 0 20 | m_LinearFogEnd: 300 21 | m_AmbientLight: {r: .200000003, g: .200000003, b: .200000003, a: 1} 22 | m_SkyboxMaterial: {fileID: 0} 23 | m_HaloStrength: .5 24 | m_FlareStrength: 1 25 | m_FlareFadeSpeed: 3 26 | m_HaloTexture: {fileID: 0} 27 | m_SpotCookie: {fileID: 0} 28 | m_ObjectHideFlags: 0 29 | --- !u!127 &3 30 | LevelGameManager: 31 | m_ObjectHideFlags: 0 32 | --- !u!157 &4 33 | LightmapSettings: 34 | m_ObjectHideFlags: 0 35 | m_LightProbes: {fileID: 0} 36 | m_Lightmaps: [] 37 | m_LightmapsMode: 1 38 | m_BakedColorSpace: 0 39 | m_UseDualLightmapsInForward: 0 40 | m_LightmapEditorSettings: 41 | m_Resolution: 50 42 | m_LastUsedResolution: 0 43 | m_TextureWidth: 1024 44 | m_TextureHeight: 1024 45 | m_BounceBoost: 1 46 | m_BounceIntensity: 1 47 | m_SkyLightColor: {r: .860000014, g: .930000007, b: 1, a: 1} 48 | m_SkyLightIntensity: 0 49 | m_Quality: 0 50 | m_Bounces: 1 51 | m_FinalGatherRays: 1000 52 | m_FinalGatherContrastThreshold: .0500000007 53 | m_FinalGatherGradientThreshold: 0 54 | m_FinalGatherInterpolationPoints: 15 55 | m_AOAmount: 0 56 | m_AOMaxDistance: .100000001 57 | m_AOContrast: 1 58 | m_LODSurfaceMappingDistance: 1 59 | m_Padding: 0 60 | m_TextureCompression: 0 61 | m_LockAtlas: 0 62 | --- !u!196 &5 63 | NavMeshSettings: 64 | m_ObjectHideFlags: 0 65 | m_BuildSettings: 66 | agentRadius: .5 67 | agentHeight: 2 68 | agentSlope: 45 69 | agentClimb: .400000006 70 | ledgeDropHeight: 0 71 | maxJumpAcrossDistance: 0 72 | accuratePlacement: 0 73 | minRegionArea: 2 74 | widthInaccuracy: 16.666666 75 | heightInaccuracy: 10 76 | m_NavMesh: {fileID: 0} 77 | --- !u!1 &606450252 78 | GameObject: 79 | m_ObjectHideFlags: 0 80 | m_PrefabParentObject: {fileID: 0} 81 | m_PrefabInternal: {fileID: 0} 82 | serializedVersion: 4 83 | m_Component: 84 | - 4: {fileID: 606450256} 85 | - 33: {fileID: 606450255} 86 | - 65: {fileID: 606450254} 87 | - 23: {fileID: 606450253} 88 | m_Layer: 0 89 | m_Name: Cube 90 | m_TagString: Untagged 91 | m_Icon: {fileID: 0} 92 | m_NavMeshLayer: 0 93 | m_StaticEditorFlags: 0 94 | m_IsActive: 1 95 | --- !u!23 &606450253 96 | Renderer: 97 | m_ObjectHideFlags: 0 98 | m_PrefabParentObject: {fileID: 0} 99 | m_PrefabInternal: {fileID: 0} 100 | m_GameObject: {fileID: 606450252} 101 | m_Enabled: 1 102 | m_CastShadows: 1 103 | m_ReceiveShadows: 1 104 | m_LightmapIndex: 255 105 | m_LightmapTilingOffset: {x: 1, y: 1, z: 0, w: 0} 106 | m_Materials: 107 | - {fileID: 10302, guid: 0000000000000000f000000000000000, type: 0} 108 | m_SubsetIndices: 109 | m_StaticBatchRoot: {fileID: 0} 110 | m_UseLightProbes: 0 111 | m_LightProbeAnchor: {fileID: 0} 112 | m_ScaleInLightmap: 1 113 | m_SortingLayerID: 0 114 | m_SortingOrder: 0 115 | --- !u!65 &606450254 116 | BoxCollider: 117 | m_ObjectHideFlags: 0 118 | m_PrefabParentObject: {fileID: 0} 119 | m_PrefabInternal: {fileID: 0} 120 | m_GameObject: {fileID: 606450252} 121 | m_Material: {fileID: 0} 122 | m_IsTrigger: 0 123 | m_Enabled: 1 124 | serializedVersion: 2 125 | m_Size: {x: 1, y: 1, z: 1} 126 | m_Center: {x: 0, y: 0, z: 0} 127 | --- !u!33 &606450255 128 | MeshFilter: 129 | m_ObjectHideFlags: 0 130 | m_PrefabParentObject: {fileID: 0} 131 | m_PrefabInternal: {fileID: 0} 132 | m_GameObject: {fileID: 606450252} 133 | m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} 134 | --- !u!4 &606450256 135 | Transform: 136 | m_ObjectHideFlags: 0 137 | m_PrefabParentObject: {fileID: 0} 138 | m_PrefabInternal: {fileID: 0} 139 | m_GameObject: {fileID: 606450252} 140 | m_LocalRotation: {x: -.199357316, y: .422789216, z: -.153068468, w: .870675623} 141 | m_LocalPosition: {x: 0, y: 0, z: 0} 142 | m_LocalScale: {x: 3.55804539, y: 3.5580461, z: 3.5580461} 143 | m_Children: [] 144 | m_Father: {fileID: 0} 145 | m_RootOrder: 2 146 | --- !u!1 &704004263 147 | GameObject: 148 | m_ObjectHideFlags: 0 149 | m_PrefabParentObject: {fileID: 0} 150 | m_PrefabInternal: {fileID: 0} 151 | serializedVersion: 4 152 | m_Component: 153 | - 4: {fileID: 704004265} 154 | - 108: {fileID: 704004264} 155 | m_Layer: 0 156 | m_Name: Directional light 157 | m_TagString: Untagged 158 | m_Icon: {fileID: 0} 159 | m_NavMeshLayer: 0 160 | m_StaticEditorFlags: 0 161 | m_IsActive: 1 162 | --- !u!108 &704004264 163 | Light: 164 | m_ObjectHideFlags: 0 165 | m_PrefabParentObject: {fileID: 0} 166 | m_PrefabInternal: {fileID: 0} 167 | m_GameObject: {fileID: 704004263} 168 | m_Enabled: 1 169 | serializedVersion: 3 170 | m_Type: 1 171 | m_Color: {r: 1, g: 1, b: 1, a: 1} 172 | m_Intensity: .5 173 | m_Range: 10 174 | m_SpotAngle: 30 175 | m_CookieSize: 10 176 | m_Shadows: 177 | m_Type: 0 178 | m_Resolution: -1 179 | m_Strength: 1 180 | m_Bias: .0500000007 181 | m_Softness: 4 182 | m_SoftnessFade: 1 183 | m_Cookie: {fileID: 0} 184 | m_DrawHalo: 0 185 | m_ActuallyLightmapped: 0 186 | m_Flare: {fileID: 0} 187 | m_RenderMode: 0 188 | m_CullingMask: 189 | serializedVersion: 2 190 | m_Bits: 4294967295 191 | m_Lightmapping: 1 192 | m_ShadowSamples: 1 193 | m_ShadowRadius: 0 194 | m_ShadowAngle: 0 195 | m_IndirectIntensity: 1 196 | m_AreaSize: {x: 1, y: 1} 197 | --- !u!4 &704004265 198 | Transform: 199 | m_ObjectHideFlags: 0 200 | m_PrefabParentObject: {fileID: 0} 201 | m_PrefabInternal: {fileID: 0} 202 | m_GameObject: {fileID: 704004263} 203 | m_LocalRotation: {x: .408217937, y: -.234569728, z: .109381661, w: .875426114} 204 | m_LocalPosition: {x: 0, y: 0, z: 0} 205 | m_LocalScale: {x: 1, y: 1, z: 1} 206 | m_Children: [] 207 | m_Father: {fileID: 0} 208 | m_RootOrder: 3 209 | --- !u!1 &1272350109 210 | GameObject: 211 | m_ObjectHideFlags: 0 212 | m_PrefabParentObject: {fileID: 0} 213 | m_PrefabInternal: {fileID: 0} 214 | serializedVersion: 4 215 | m_Component: 216 | - 4: {fileID: 1272350111} 217 | - 114: {fileID: 1272350110} 218 | m_Layer: 0 219 | m_Name: CorrespondenceAcquisition 220 | m_TagString: Untagged 221 | m_Icon: {fileID: 0} 222 | m_NavMeshLayer: 0 223 | m_StaticEditorFlags: 0 224 | m_IsActive: 1 225 | --- !u!114 &1272350110 226 | MonoBehaviour: 227 | m_ObjectHideFlags: 0 228 | m_PrefabParentObject: {fileID: 0} 229 | m_PrefabInternal: {fileID: 0} 230 | m_GameObject: {fileID: 1272350109} 231 | m_Enabled: 1 232 | m_EditorHideFlags: 0 233 | m_Script: {fileID: 11500000, guid: 55b7390b601b1b849902e32ce423f074, type: 3} 234 | m_Name: 235 | m_EditorClassIdentifier: 236 | _crosshairTexture: {fileID: 2800000, guid: a8ead6295272c24479f2c028f25ba604, type: 3} 237 | _mainCamera: {fileID: 1497708957} 238 | --- !u!4 &1272350111 239 | Transform: 240 | m_ObjectHideFlags: 0 241 | m_PrefabParentObject: {fileID: 0} 242 | m_PrefabInternal: {fileID: 0} 243 | m_GameObject: {fileID: 1272350109} 244 | m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} 245 | m_LocalPosition: {x: 0, y: 0, z: 0} 246 | m_LocalScale: {x: 1, y: 1, z: 1} 247 | m_Children: [] 248 | m_Father: {fileID: 0} 249 | m_RootOrder: 1 250 | --- !u!1 &1497708953 251 | GameObject: 252 | m_ObjectHideFlags: 0 253 | m_PrefabParentObject: {fileID: 0} 254 | m_PrefabInternal: {fileID: 0} 255 | serializedVersion: 4 256 | m_Component: 257 | - 4: {fileID: 1497708958} 258 | - 20: {fileID: 1497708957} 259 | - 92: {fileID: 1497708956} 260 | - 124: {fileID: 1497708955} 261 | - 81: {fileID: 1497708954} 262 | - 114: {fileID: 1497708959} 263 | m_Layer: 0 264 | m_Name: Main Camera 265 | m_TagString: MainCamera 266 | m_Icon: {fileID: 0} 267 | m_NavMeshLayer: 0 268 | m_StaticEditorFlags: 0 269 | m_IsActive: 1 270 | --- !u!81 &1497708954 271 | AudioListener: 272 | m_ObjectHideFlags: 0 273 | m_PrefabParentObject: {fileID: 0} 274 | m_PrefabInternal: {fileID: 0} 275 | m_GameObject: {fileID: 1497708953} 276 | m_Enabled: 1 277 | --- !u!124 &1497708955 278 | Behaviour: 279 | m_ObjectHideFlags: 0 280 | m_PrefabParentObject: {fileID: 0} 281 | m_PrefabInternal: {fileID: 0} 282 | m_GameObject: {fileID: 1497708953} 283 | m_Enabled: 1 284 | --- !u!92 &1497708956 285 | Behaviour: 286 | m_ObjectHideFlags: 0 287 | m_PrefabParentObject: {fileID: 0} 288 | m_PrefabInternal: {fileID: 0} 289 | m_GameObject: {fileID: 1497708953} 290 | m_Enabled: 1 291 | --- !u!20 &1497708957 292 | Camera: 293 | m_ObjectHideFlags: 0 294 | m_PrefabParentObject: {fileID: 0} 295 | m_PrefabInternal: {fileID: 0} 296 | m_GameObject: {fileID: 1497708953} 297 | m_Enabled: 1 298 | serializedVersion: 2 299 | m_ClearFlags: 1 300 | m_BackGroundColor: {r: .192156866, g: .301960796, b: .474509805, a: .0196078438} 301 | m_NormalizedViewPortRect: 302 | serializedVersion: 2 303 | x: 0 304 | y: 0 305 | width: 1 306 | height: 1 307 | near clip plane: .300000012 308 | far clip plane: 1000 309 | field of view: 60 310 | orthographic: 0 311 | orthographic size: 5 312 | m_Depth: -1 313 | m_CullingMask: 314 | serializedVersion: 2 315 | m_Bits: 4294967295 316 | m_RenderingPath: -1 317 | m_TargetTexture: {fileID: 0} 318 | m_TargetDisplay: 0 319 | m_HDR: 0 320 | m_OcclusionCulling: 1 321 | m_StereoConvergence: 10 322 | m_StereoSeparation: .0219999999 323 | --- !u!4 &1497708958 324 | Transform: 325 | m_ObjectHideFlags: 0 326 | m_PrefabParentObject: {fileID: 0} 327 | m_PrefabInternal: {fileID: 0} 328 | m_GameObject: {fileID: 1497708953} 329 | m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} 330 | m_LocalPosition: {x: 0, y: 1, z: -10} 331 | m_LocalScale: {x: 1, y: 1, z: 1} 332 | m_Children: [] 333 | m_Father: {fileID: 0} 334 | m_RootOrder: 0 335 | --- !u!114 &1497708959 336 | MonoBehaviour: 337 | m_ObjectHideFlags: 0 338 | m_PrefabParentObject: {fileID: 0} 339 | m_PrefabInternal: {fileID: 0} 340 | m_GameObject: {fileID: 1497708953} 341 | m_Enabled: 1 342 | m_EditorHideFlags: 0 343 | m_Script: {fileID: 11500000, guid: 94f2105ed09d0f4409a98b567f69b355, type: 3} 344 | m_Name: 345 | m_EditorClassIdentifier: 346 | --- !u!1 &1866372080 347 | GameObject: 348 | m_ObjectHideFlags: 0 349 | m_PrefabParentObject: {fileID: 0} 350 | m_PrefabInternal: {fileID: 0} 351 | serializedVersion: 4 352 | m_Component: 353 | - 4: {fileID: 1866372082} 354 | - 114: {fileID: 1866372081} 355 | m_Layer: 0 356 | m_Name: SaveLoadPoints 357 | m_TagString: Untagged 358 | m_Icon: {fileID: 0} 359 | m_NavMeshLayer: 0 360 | m_StaticEditorFlags: 0 361 | m_IsActive: 1 362 | --- !u!114 &1866372081 363 | MonoBehaviour: 364 | m_ObjectHideFlags: 0 365 | m_PrefabParentObject: {fileID: 0} 366 | m_PrefabInternal: {fileID: 0} 367 | m_GameObject: {fileID: 1866372080} 368 | m_Enabled: 1 369 | m_EditorHideFlags: 0 370 | m_Script: {fileID: 11500000, guid: 8e57bdc6f7707ef40befbe6413fec952, type: 3} 371 | m_Name: 372 | m_EditorClassIdentifier: 373 | pointsHolder: {fileID: 1272350110} 374 | --- !u!4 &1866372082 375 | Transform: 376 | m_ObjectHideFlags: 0 377 | m_PrefabParentObject: {fileID: 0} 378 | m_PrefabInternal: {fileID: 0} 379 | m_GameObject: {fileID: 1866372080} 380 | m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} 381 | m_LocalPosition: {x: 0, y: 0, z: 0} 382 | m_LocalScale: {x: 1, y: 1, z: 1} 383 | m_Children: [] 384 | m_Father: {fileID: 0} 385 | m_RootOrder: 4 386 | -------------------------------------------------------------------------------- /Assets/CalibrationExample.unity.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 4fd0149796907c445a40584fee24c739 3 | DefaultImporter: 4 | userData: 5 | -------------------------------------------------------------------------------- /Assets/CorrespondenceAcquisition.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | 5 | // TODO: refactor so _height - pos.y is abstracted away 6 | 7 | public class CorrespondenceAcquisition : MonoBehaviour 8 | { 9 | public Texture2D _crosshairTexture; 10 | public Camera _mainCamera; 11 | 12 | private Calibration _calibrator; 13 | private int _minNumberOfPoints = 7; 14 | private List _objectPositions = new List(); 15 | private List _imagePositions = new List(); 16 | private bool _occludeWorld; 17 | private int _width; 18 | private int _height; 19 | private double _reprojectionError; 20 | private int? _dragging; 21 | private bool _calibrating; 22 | 23 | private static int IMAGE_POINT_MARKER_SIZE = 5; 24 | private static int IMAGE_POINT_HIGHLIGHTED_SIZE = 10; 25 | private static string CALIB_SPHERE_TAG = "CalibrationSphere"; 26 | 27 | void Start() 28 | { 29 | _occludeWorld = false; 30 | _reprojectionError = 0.0; 31 | _height = (int)Screen.height; 32 | _width = (int)Screen.width; 33 | _calibrator = new Calibration(_mainCamera); 34 | _calibrating = true; 35 | } 36 | 37 | void OnGUI() 38 | { 39 | if (!_calibrating) 40 | return; 41 | 42 | if (_occludeWorld) 43 | OccludeScreen(); 44 | 45 | _imagePositions.ForEach(delegate(Vector3 position) { DrawCrosshairImage(position, IMAGE_POINT_MARKER_SIZE); }); 46 | if (ImagePointHighlighted()) 47 | DrawCrosshairImage(_imagePositions[ImagePointMouseHooveringOver().Value], IMAGE_POINT_HIGHLIGHTED_SIZE); 48 | 49 | GUI.Box(new Rect(10, 10, 150, 90), "# points: " + _imagePositions.Count.ToString() + "\nReProj error: " + string.Format("{0:f2}", _reprojectionError)); 50 | } 51 | 52 | private void DrawCrosshairImage(Vector3 position, int imagePointMarkerWidth) 53 | { 54 | GUI.DrawTexture(new Rect(position.x - imagePointMarkerWidth, position.y - imagePointMarkerWidth, imagePointMarkerWidth * 2, imagePointMarkerWidth * 2), _crosshairTexture); 55 | } 56 | 57 | private void OccludeScreen() 58 | { 59 | Texture2D blackTexture = new Texture2D(1, 1); 60 | blackTexture.SetPixel(0, 0, Color.black); 61 | GUI.DrawTexture(new Rect(0, 0, _width, _height), blackTexture); 62 | } 63 | 64 | void Update() 65 | { 66 | if (Input.GetKeyDown(KeyCode.Escape)) 67 | Application.Quit(); 68 | 69 | if (Input.GetKeyDown(KeyCode.Space)) 70 | ToggleCalibrating(); 71 | 72 | if (Input.GetMouseButtonDown(0)) 73 | { 74 | if (ImagePointHighlighted()) 75 | { 76 | _dragging = ImagePointMouseHooveringOver(); 77 | } 78 | else 79 | { 80 | if (_imagePositions.Count == _objectPositions.Count) 81 | { 82 | // We have the same number of object and image positions, so we are starting a new correspondence. First is the object position 83 | CaptureWorldPoint(); 84 | } 85 | else 86 | { 87 | // we already have an object position, now we collect the 2D correspondence 88 | CaptureImagePoint(); 89 | TriggerCalibration(); 90 | } 91 | } 92 | } 93 | 94 | if (Input.GetMouseButtonUp(0)) 95 | { 96 | _dragging = null; 97 | TriggerCalibration(); 98 | } 99 | 100 | if (_dragging != null) 101 | { 102 | var pos = Input.mousePosition; 103 | pos.y = _height - pos.y; 104 | _imagePositions[_dragging.Value] = pos; 105 | } 106 | } 107 | 108 | private void ToggleCalibrating() 109 | { 110 | _calibrating = !_calibrating; 111 | 112 | foreach (GameObject sphere in GameObject.FindGameObjectsWithTag(CALIB_SPHERE_TAG)) 113 | { 114 | sphere.renderer.enabled = _calibrating; 115 | } 116 | } 117 | 118 | private bool ImagePointHighlighted() 119 | { 120 | return ImagePointMouseHooveringOver() != null; 121 | } 122 | 123 | private int? ImagePointMouseHooveringOver() 124 | { 125 | Vector3 pos = Input.mousePosition; 126 | int? imagePosMatch = null; 127 | for (int i = 0; i < _imagePositions.Count; i++) 128 | { 129 | Vector3 imagePos = _imagePositions[i]; 130 | if (Mathf.Abs(imagePos.x - pos.x) + Mathf.Abs(imagePos.y - (_height - pos.y)) < 5) 131 | { 132 | imagePosMatch = i; 133 | } 134 | } 135 | return imagePosMatch; 136 | } 137 | 138 | private void CaptureWorldPoint() 139 | { 140 | Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); 141 | RaycastHit hit3d; 142 | if (Physics.Raycast(ray, out hit3d)) 143 | { 144 | CreateSphereAt(hit3d.point); 145 | _objectPositions.Add(hit3d.point); 146 | _occludeWorld = true; 147 | } 148 | } 149 | 150 | private void CaptureImagePoint() 151 | { 152 | Vector3 pos = Input.mousePosition; 153 | pos.y = _height - pos.y; // note the screen pos starts bottom left. We want top left origin 154 | _imagePositions.Add(pos); 155 | _occludeWorld = false; 156 | } 157 | 158 | private static void CreateSphereAt(Vector3 point) 159 | { 160 | GameObject sphere = GameObject.CreatePrimitive(PrimitiveType.Sphere); 161 | sphere.transform.position = point; 162 | sphere.tag = CALIB_SPHERE_TAG; 163 | } 164 | 165 | private void TriggerCalibration() 166 | { 167 | if (_imagePositions.Count < _minNumberOfPoints) 168 | return; 169 | if (_imagePositions.Count != _objectPositions.Count) 170 | return; 171 | 172 | _reprojectionError = _calibrator.calibrateFromCorrespondences(_imagePositions, _objectPositions); 173 | } 174 | 175 | public List GetImagePoints() 176 | { 177 | return _imagePositions; 178 | } 179 | 180 | public List GetWorldPoints() 181 | { 182 | return _objectPositions; 183 | } 184 | 185 | public void ReplayRecordedPoints(List worldPoints, List imgPoints) 186 | { 187 | _imagePositions = imgPoints; 188 | _objectPositions = worldPoints; 189 | foreach (var point in worldPoints) 190 | CreateSphereAt(point); 191 | TriggerCalibration(); 192 | } 193 | } -------------------------------------------------------------------------------- /Assets/CorrespondenceAcquisition.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 55b7390b601b1b849902e32ce423f074 3 | MonoImporter: 4 | serializedVersion: 2 5 | defaultReferences: [] 6 | executionOrder: 0 7 | icon: {instanceID: 0} 8 | userData: 9 | -------------------------------------------------------------------------------- /Assets/DrawLine.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | 4 | /* 5 | * Attribution: Part of this solution is taken from http://answers.unity3d.com/questions/686293/using-gl-to-draw-after-ongui.html 6 | */ 7 | 8 | public class DrawLine : MonoBehaviour { 9 | private Vector3 _xyPos; 10 | private Material _lineMaterial; 11 | 12 | void Start() 13 | { 14 | CreateLineMaterial(); 15 | } 16 | 17 | private void CreateLineMaterial() 18 | { 19 | _lineMaterial = new Material("Shader \"Lines/Colored Blended\" {" + 20 | "SubShader { Pass { " + 21 | " Blend SrcAlpha OneMinusSrcAlpha " + 22 | " ZWrite Off Cull Off Fog { Mode Off } " + 23 | " BindChannels {" + 24 | " Bind \"vertex\", vertex Bind \"color\", color }" + 25 | "} } }"); 26 | _lineMaterial.hideFlags = HideFlags.HideAndDontSave; 27 | _lineMaterial.shader.hideFlags = HideFlags.HideAndDontSave; 28 | } 29 | 30 | void Update() 31 | { 32 | _xyPos = Input.mousePosition; 33 | } 34 | 35 | IEnumerator OnPostRenderMeth() 36 | { 37 | yield return new WaitForEndOfFrame(); 38 | 39 | GL.PushMatrix(); 40 | GL.LoadPixelMatrix(); 41 | _lineMaterial.SetPass(0); 42 | GL.LoadOrtho(); 43 | GL.Begin(GL.LINES); 44 | GL.Color(Color.red); 45 | GL.Vertex(new Vector3(_xyPos.x / (float)Screen.width, 0, 0)); 46 | GL.Vertex(new Vector3(_xyPos.x / (float)Screen.width, 1, 0)); 47 | 48 | GL.Color(Color.green); 49 | GL.Vertex(new Vector3(0, _xyPos.y / (float)Screen.height, 0)); 50 | GL.Vertex(new Vector3(1, _xyPos.y / (float)Screen.height, 0)); 51 | 52 | GL.End(); 53 | GL.PopMatrix(); 54 | } 55 | 56 | void OnPostRender() 57 | { 58 | StartCoroutine(OnPostRenderMeth()); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /Assets/DrawLine.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 94f2105ed09d0f4409a98b567f69b355 3 | MonoImporter: 4 | serializedVersion: 2 5 | defaultReferences: [] 6 | executionOrder: 0 7 | icon: {instanceID: 0} 8 | userData: 9 | -------------------------------------------------------------------------------- /Assets/Plugins.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c2c0c24c1b30801428cb7cbe1f52a6f8 3 | folderAsset: yes 4 | DefaultImporter: 5 | userData: 6 | -------------------------------------------------------------------------------- /Assets/Plugins/x86.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: cf57a03d9a9f4d746844ce6cde0521cf 3 | folderAsset: yes 4 | DefaultImporter: 5 | userData: 6 | -------------------------------------------------------------------------------- /Assets/Plugins/x86/OpenCvSharp.CPlusPlus.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewmacquarrie/UnityProjectionMapping/118a247d1956621eab0d821f441ad98d10642612/Assets/Plugins/x86/OpenCvSharp.CPlusPlus.dll -------------------------------------------------------------------------------- /Assets/Plugins/x86/OpenCvSharp.CPlusPlus.dll.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /Assets/Plugins/x86/OpenCvSharp.CPlusPlus.dll.config.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 1422b1edecaa7504a935e302326f18ae 3 | DefaultImporter: 4 | userData: 5 | -------------------------------------------------------------------------------- /Assets/Plugins/x86/OpenCvSharp.CPlusPlus.dll.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 7f66ff72ff162f0448c7adb5c1bfc638 3 | MonoAssemblyImporter: 4 | serializedVersion: 1 5 | iconMap: {} 6 | executionOrder: {} 7 | userData: 8 | -------------------------------------------------------------------------------- /Assets/Plugins/x86/OpenCvSharp.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewmacquarrie/UnityProjectionMapping/118a247d1956621eab0d821f441ad98d10642612/Assets/Plugins/x86/OpenCvSharp.dll -------------------------------------------------------------------------------- /Assets/Plugins/x86/OpenCvSharp.dll.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /Assets/Plugins/x86/OpenCvSharp.dll.config.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: f168e414716b68b47ae13ea6d8c12214 3 | DefaultImporter: 4 | userData: 5 | -------------------------------------------------------------------------------- /Assets/Plugins/x86/OpenCvSharp.dll.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: a91fdb4c06497854d8ee5dd35e96b7b2 3 | MonoAssemblyImporter: 4 | serializedVersion: 1 5 | iconMap: {} 6 | executionOrder: {} 7 | userData: 8 | -------------------------------------------------------------------------------- /Assets/Plugins/x86/OpenCvSharpExtern.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewmacquarrie/UnityProjectionMapping/118a247d1956621eab0d821f441ad98d10642612/Assets/Plugins/x86/OpenCvSharpExtern.dll -------------------------------------------------------------------------------- /Assets/Plugins/x86/OpenCvSharpExtern.dll.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: bdd1b27080e5536478fdb3ddf70ce029 3 | DefaultImporter: 4 | userData: 5 | -------------------------------------------------------------------------------- /Assets/PointsRecordReplay.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using Assets; 5 | using System.Xml.Serialization; 6 | using System.IO; 7 | #if UNITY_EDITOR 8 | using UnityEditor; 9 | #endif 10 | 11 | public class PointsRecordReplay : MonoBehaviour { 12 | public CorrespondenceAcquisition pointsHolder; 13 | 14 | void Start () { 15 | } 16 | 17 | void Update () { 18 | if (Input.GetKeyDown(KeyCode.R)) 19 | { 20 | RecordCurrentPoints(); 21 | Debug.Log("Recording complete"); 22 | } 23 | 24 | if (Input.GetKeyDown(KeyCode.P)) 25 | { 26 | LoadSavedPoints(); 27 | Debug.Log("Loading complete"); 28 | } 29 | } 30 | 31 | private void LoadSavedPoints() 32 | { 33 | string fileName; 34 | #if UNITY_EDITOR 35 | fileName = EditorUtility.OpenFilePanel("Choose the file path", "", "xml"); 36 | #endif 37 | #if UNITY_EDITOR == false 38 | fileName = "recording.xml"; 39 | #endif 40 | var recording = Recording.LoadFromFile(fileName); 41 | pointsHolder.ReplayRecordedPoints(recording.worldPointsV3, recording.ImagePointsV3((double)Screen.width, (double)Screen.height)); 42 | } 43 | 44 | private void RecordCurrentPoints() 45 | { 46 | string fileName; 47 | #if UNITY_EDITOR 48 | fileName = EditorUtility.SaveFilePanel("Choose the file path", "", "pointsRecording", "xml"); 49 | #endif 50 | #if UNITY_EDITOR == false 51 | fileName = "recording.xml"; 52 | #endif 53 | SaveToFile(pointsHolder.GetImagePoints(), pointsHolder.GetWorldPoints(), fileName); 54 | } 55 | 56 | private static void SaveToFile(List imagePoints, List worldPoints, string fileName) 57 | { 58 | var recording = new Recording(imagePoints, worldPoints, (double)Screen.width, (double)Screen.height); 59 | recording.SaveToFile(fileName); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Assets/PointsRecordReplay.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 8e57bdc6f7707ef40befbe6413fec952 3 | MonoImporter: 4 | serializedVersion: 2 5 | defaultReferences: [] 6 | executionOrder: 0 7 | icon: {instanceID: 0} 8 | userData: 9 | -------------------------------------------------------------------------------- /Assets/ProjectionController.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | 4 | public class ProjectionController : MonoBehaviour { 5 | void Start () { 6 | 7 | } 8 | 9 | void Update () { 10 | if (Input.GetKeyDown(KeyCode.T)) 11 | { 12 | Projector p = GetComponent(); 13 | p.enabled = !p.enabled; 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Assets/ProjectionController.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 07dc08fe2652d294fb3be6900f78526f 3 | MonoImporter: 4 | serializedVersion: 2 5 | defaultReferences: [] 6 | executionOrder: 0 7 | icon: {instanceID: 0} 8 | userData: 9 | -------------------------------------------------------------------------------- /Assets/Recording.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Xml.Serialization; 7 | using UnityEngine; 8 | 9 | namespace Assets 10 | { 11 | [Serializable] 12 | public class Recording 13 | { 14 | // needed for serialization 15 | public Recording() { } 16 | 17 | public Recording(List ip, List wp, double width, double height){ 18 | List normalizedImagePoints = NormalizeImagePoints(ip, width, height); 19 | imagePoints = ConvertToSerializableList(normalizedImagePoints); 20 | worldPoints = ConvertToSerializableList(wp); 21 | } 22 | 23 | private static List NormalizeImagePoints(List ip, double width, double height) 24 | { 25 | List normalizedImagePoints = new List(); 26 | foreach (Vector3 point in ip) 27 | { 28 | normalizedImagePoints.Add(new Vector3(point.x / (float) width, point.y / (float) height, point.z)); 29 | } 30 | return normalizedImagePoints; 31 | } 32 | 33 | public List worldPoints; 34 | public List imagePoints; 35 | 36 | public List worldPointsV3 37 | { 38 | get { 39 | return ConvertToV3(worldPoints); 40 | } 41 | } 42 | 43 | public List ImagePointsV3(double width, double height) 44 | { 45 | List denormalizedPoints = new List(); 46 | foreach (SerializableVector3 point in imagePoints) 47 | { 48 | denormalizedPoints.Add(new SerializableVector3(point.X * width, point.Y * height, point.Z)); 49 | } 50 | return ConvertToV3(denormalizedPoints); 51 | } 52 | 53 | private List ConvertToV3(List sv3List) 54 | { 55 | var convList = new List(); 56 | foreach (var sv3 in sv3List) 57 | { 58 | convList.Add(sv3.Vector3); 59 | } 60 | return convList; 61 | } 62 | 63 | private static List ConvertToSerializableList(List imagePoints) 64 | { 65 | List serializableImageList = new List(); 66 | foreach (var imagePoint in imagePoints) 67 | { 68 | serializableImageList.Add(new SerializableVector3(imagePoint)); 69 | } 70 | return serializableImageList; 71 | } 72 | 73 | public void SaveToFile(string fileName) 74 | { 75 | System.IO.File.WriteAllText(fileName, this.SerializeObject()); 76 | } 77 | 78 | public static Recording LoadFromFile(string fileName) 79 | { 80 | var data = System.IO.File.ReadAllText(fileName); 81 | XmlSerializer xmlSerializer = new XmlSerializer(typeof(Recording)); 82 | using (StringReader textReader = new StringReader(data)) 83 | { 84 | return (Recording) xmlSerializer.Deserialize(textReader); 85 | } 86 | } 87 | } 88 | 89 | 90 | 91 | } 92 | -------------------------------------------------------------------------------- /Assets/Recording.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 83c76bc295d63644caa7ba6558c6509a 3 | MonoImporter: 4 | serializedVersion: 2 5 | defaultReferences: [] 6 | executionOrder: 0 7 | icon: {instanceID: 0} 8 | userData: 9 | -------------------------------------------------------------------------------- /Assets/Rotation.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace Assets 7 | { 8 | class Rotation 9 | { 10 | public double X; 11 | public double Y; 12 | public double Z; 13 | 14 | public Rotation(double x, double y, double z) 15 | { 16 | X = x; 17 | Y = y; 18 | Z = z; 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Assets/Rotation.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 338c181b100d65d4d8ef72d8791436da 3 | MonoImporter: 4 | serializedVersion: 2 5 | defaultReferences: [] 6 | executionOrder: 0 7 | icon: {instanceID: 0} 8 | userData: 9 | -------------------------------------------------------------------------------- /Assets/RotationConversion.cs: -------------------------------------------------------------------------------- 1 | using OpenCvSharp; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | 7 | namespace Assets 8 | { 9 | class RotationConversion 10 | { 11 | // Maths comes from Python's Relax http://svn.gna.org/svn/relax/1.3/maths_fns/rotation_matrix.py 12 | 13 | static float EULER_EPSILON = 0.00005f; 14 | 15 | static public Rotation RotationMatrixToEulerZXY(CvMat R) 16 | { 17 | var i = 2; 18 | var j = 0; // EULER_NEXT[2] 19 | var k = 1; // EULER_NEXT[3] 20 | 21 | var cos_beta = Math.Sqrt(Math.Pow(R[i, i], 2) + Math.Pow(R[j, i], 2)); 22 | 23 | double alpha, beta, gamma; 24 | if (cos_beta > EULER_EPSILON) 25 | { 26 | alpha = Math.Atan2(R[k, j], R[k, k]); 27 | beta = Math.Atan2(-R[k, i], cos_beta); 28 | gamma = Math.Atan2(R[j, i], R[i, i]); 29 | } 30 | else 31 | { 32 | alpha = Math.Atan2(-R[j, k], R[j, j]); 33 | beta = Math.Atan2(-R[k, i], cos_beta); 34 | gamma = 0.0; 35 | } 36 | 37 | alpha = wrap_angles(alpha, 0.0, 2.0 * Math.PI); // Z 38 | beta = wrap_angles(beta, 0.0, 2.0 * Math.PI); // X 39 | gamma = wrap_angles(gamma, 0.0, 2.0 * Math.PI); // Y 40 | 41 | // errr... 180 - Z result seems right. Why? 42 | return new Rotation(RadianToDegree(beta), RadianToDegree(gamma), 180.0 - RadianToDegree(alpha)); 43 | } 44 | 45 | static double wrap_angles(double angle, double lower, double upper) 46 | { 47 | var window = 2 * Math.PI; 48 | if(window - (upper - lower) > 1e-7) 49 | throw new ArgumentException(); 50 | 51 | while(true) { 52 | if(angle > upper) 53 | angle = angle - window; 54 | else if (angle < lower) 55 | angle = angle + window; 56 | else 57 | break; 58 | } 59 | 60 | return angle; 61 | } 62 | 63 | private static double RadianToDegree(double angle) 64 | { 65 | return angle * (180.0 / Math.PI); 66 | } 67 | 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /Assets/RotationConversion.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: ad14219c1edbb0a44986d1f1194bc1cb 3 | MonoImporter: 4 | serializedVersion: 2 5 | defaultReferences: [] 6 | executionOrder: 0 7 | icon: {instanceID: 0} 8 | userData: 9 | -------------------------------------------------------------------------------- /Assets/SerializableVector3.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using UnityEngine; 6 | 7 | namespace Assets 8 | { 9 | [Serializable] 10 | public class SerializableVector3 11 | { 12 | public double X; 13 | public double Y; 14 | public double Z; 15 | 16 | public Vector3 Vector3 17 | { 18 | get 19 | { 20 | return new Vector3((float)X, (float)Y, (float)Z); 21 | } 22 | } 23 | 24 | public SerializableVector3() { } 25 | public SerializableVector3(Vector3 vector) 26 | { 27 | double val; 28 | X = double.TryParse(vector.x.ToString(), out val) ? val : 0.0; 29 | Y = double.TryParse(vector.y.ToString(), out val) ? val : 0.0; 30 | Z = double.TryParse(vector.z.ToString(), out val) ? val : 0.0; 31 | } 32 | public SerializableVector3(double x, double y, double z) 33 | { 34 | X = x; 35 | Y = y; 36 | Z = z; 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Assets/SerializableVector3.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 2a036bb76477e0d4d8031d7f446c1e44 3 | MonoImporter: 4 | serializedVersion: 2 5 | defaultReferences: [] 6 | executionOrder: 0 7 | icon: {instanceID: 0} 8 | userData: 9 | -------------------------------------------------------------------------------- /Assets/SerializeExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Xml.Serialization; 6 | using System.IO; 7 | 8 | namespace Assets 9 | { 10 | public static class SerializeExtensions 11 | { 12 | public static string SerializeObject(this T toSerialize) 13 | { 14 | using (StringWriter textWriter = new StringWriter()) 15 | { 16 | XmlSerializer xmlSerializer = new XmlSerializer(toSerialize.GetType()); 17 | xmlSerializer.Serialize(textWriter, toSerialize); 18 | return textWriter.ToString(); 19 | } 20 | } 21 | 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Assets/SerializeExtensions.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 9ab32df9b7aa12949a25ec0a2f330887 3 | MonoImporter: 4 | serializedVersion: 2 5 | defaultReferences: [] 6 | executionOrder: 0 7 | icon: {instanceID: 0} 8 | userData: 9 | -------------------------------------------------------------------------------- /Assets/crosshair.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewmacquarrie/UnityProjectionMapping/118a247d1956621eab0d821f441ad98d10642612/Assets/crosshair.png -------------------------------------------------------------------------------- /Assets/crosshair.png.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: a8ead6295272c24479f2c028f25ba604 3 | TextureImporter: 4 | fileIDToRecycleName: {} 5 | serializedVersion: 2 6 | mipmaps: 7 | mipMapMode: 0 8 | enableMipMap: 1 9 | linearTexture: 0 10 | correctGamma: 0 11 | fadeOut: 0 12 | borderMipMap: 0 13 | mipMapFadeDistanceStart: 1 14 | mipMapFadeDistanceEnd: 3 15 | bumpmap: 16 | convertToNormalMap: 0 17 | externalNormalMap: 0 18 | heightScale: .25 19 | normalMapFilter: 0 20 | isReadable: 0 21 | grayScaleToAlpha: 0 22 | generateCubemap: 0 23 | seamlessCubemap: 0 24 | textureFormat: -1 25 | maxTextureSize: 128 26 | textureSettings: 27 | filterMode: -1 28 | aniso: -1 29 | mipBias: -1 30 | wrapMode: -1 31 | nPOTScale: 1 32 | lightmap: 0 33 | compressionQuality: 50 34 | spriteMode: 0 35 | spriteExtrude: 1 36 | spriteMeshType: 1 37 | alignment: 0 38 | spritePivot: {x: .5, y: .5} 39 | spriteBorder: {x: 0, y: 0, z: 0, w: 0} 40 | spritePixelsToUnits: 100 41 | alphaIsTransparency: 0 42 | textureType: -1 43 | buildTargetSettings: [] 44 | spriteSheet: 45 | sprites: [] 46 | spritePackingTag: 47 | userData: 48 | -------------------------------------------------------------------------------- /OpenCVDlls/opencv_calib3d2410.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewmacquarrie/UnityProjectionMapping/118a247d1956621eab0d821f441ad98d10642612/OpenCVDlls/opencv_calib3d2410.dll -------------------------------------------------------------------------------- /OpenCVDlls/opencv_core2410.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewmacquarrie/UnityProjectionMapping/118a247d1956621eab0d821f441ad98d10642612/OpenCVDlls/opencv_core2410.dll -------------------------------------------------------------------------------- /OpenCVDlls/opencv_features2d2410.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewmacquarrie/UnityProjectionMapping/118a247d1956621eab0d821f441ad98d10642612/OpenCVDlls/opencv_features2d2410.dll -------------------------------------------------------------------------------- /OpenCVDlls/opencv_flann2410.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewmacquarrie/UnityProjectionMapping/118a247d1956621eab0d821f441ad98d10642612/OpenCVDlls/opencv_flann2410.dll -------------------------------------------------------------------------------- /OpenCVDlls/opencv_imgproc2410.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewmacquarrie/UnityProjectionMapping/118a247d1956621eab0d821f441ad98d10642612/OpenCVDlls/opencv_imgproc2410.dll -------------------------------------------------------------------------------- /ProjectSettings/AudioManager.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!11 &1 4 | AudioManager: 5 | m_ObjectHideFlags: 0 6 | m_Volume: 1 7 | Rolloff Scale: 1 8 | m_SpeedOfSound: 347 9 | Doppler Factor: 1 10 | Default Speaker Mode: 2 11 | m_DSPBufferSize: 0 12 | m_DisableAudio: 0 13 | -------------------------------------------------------------------------------- /ProjectSettings/DynamicsManager.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!55 &1 4 | PhysicsManager: 5 | m_ObjectHideFlags: 0 6 | m_Gravity: {x: 0, y: -9.81000042, z: 0} 7 | m_DefaultMaterial: {fileID: 0} 8 | m_BounceThreshold: 2 9 | m_SleepVelocity: .150000006 10 | m_SleepAngularVelocity: .140000001 11 | m_MaxAngularVelocity: 7 12 | m_MinPenetrationForPenalty: .00999999978 13 | m_SolverIterationCount: 6 14 | m_RaycastsHitTriggers: 1 15 | m_LayerCollisionMatrix: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 16 | -------------------------------------------------------------------------------- /ProjectSettings/EditorBuildSettings.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!1045 &1 4 | EditorBuildSettings: 5 | m_ObjectHideFlags: 0 6 | serializedVersion: 2 7 | m_Scenes: [] 8 | -------------------------------------------------------------------------------- /ProjectSettings/EditorSettings.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!159 &1 4 | EditorSettings: 5 | m_ObjectHideFlags: 0 6 | serializedVersion: 3 7 | m_ExternalVersionControlSupport: Visible Meta Files 8 | m_SerializationMode: 2 9 | m_WebSecurityEmulationEnabled: 0 10 | m_WebSecurityEmulationHostUrl: http://www.mydomain.com/mygame.unity3d 11 | m_DefaultBehaviorMode: 0 12 | m_SpritePackerMode: 0 13 | -------------------------------------------------------------------------------- /ProjectSettings/GraphicsSettings.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!30 &1 4 | GraphicsSettings: 5 | m_ObjectHideFlags: 0 6 | m_AlwaysIncludedShaders: 7 | - {fileID: 7, guid: 0000000000000000f000000000000000, type: 0} 8 | -------------------------------------------------------------------------------- /ProjectSettings/InputManager.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!13 &1 4 | InputManager: 5 | m_ObjectHideFlags: 0 6 | m_Axes: 7 | - serializedVersion: 3 8 | m_Name: Horizontal 9 | descriptiveName: 10 | descriptiveNegativeName: 11 | negativeButton: left 12 | positiveButton: right 13 | altNegativeButton: a 14 | altPositiveButton: d 15 | gravity: 3 16 | dead: .00100000005 17 | sensitivity: 3 18 | snap: 1 19 | invert: 0 20 | type: 0 21 | axis: 0 22 | joyNum: 0 23 | - serializedVersion: 3 24 | m_Name: Vertical 25 | descriptiveName: 26 | descriptiveNegativeName: 27 | negativeButton: down 28 | positiveButton: up 29 | altNegativeButton: s 30 | altPositiveButton: w 31 | gravity: 3 32 | dead: .00100000005 33 | sensitivity: 3 34 | snap: 1 35 | invert: 0 36 | type: 0 37 | axis: 0 38 | joyNum: 0 39 | - serializedVersion: 3 40 | m_Name: Fire1 41 | descriptiveName: 42 | descriptiveNegativeName: 43 | negativeButton: 44 | positiveButton: left ctrl 45 | altNegativeButton: 46 | altPositiveButton: mouse 0 47 | gravity: 1000 48 | dead: .00100000005 49 | sensitivity: 1000 50 | snap: 0 51 | invert: 0 52 | type: 0 53 | axis: 0 54 | joyNum: 0 55 | - serializedVersion: 3 56 | m_Name: Fire2 57 | descriptiveName: 58 | descriptiveNegativeName: 59 | negativeButton: 60 | positiveButton: left alt 61 | altNegativeButton: 62 | altPositiveButton: mouse 1 63 | gravity: 1000 64 | dead: .00100000005 65 | sensitivity: 1000 66 | snap: 0 67 | invert: 0 68 | type: 0 69 | axis: 0 70 | joyNum: 0 71 | - serializedVersion: 3 72 | m_Name: Fire3 73 | descriptiveName: 74 | descriptiveNegativeName: 75 | negativeButton: 76 | positiveButton: left cmd 77 | altNegativeButton: 78 | altPositiveButton: mouse 2 79 | gravity: 1000 80 | dead: .00100000005 81 | sensitivity: 1000 82 | snap: 0 83 | invert: 0 84 | type: 0 85 | axis: 0 86 | joyNum: 0 87 | - serializedVersion: 3 88 | m_Name: Jump 89 | descriptiveName: 90 | descriptiveNegativeName: 91 | negativeButton: 92 | positiveButton: space 93 | altNegativeButton: 94 | altPositiveButton: 95 | gravity: 1000 96 | dead: .00100000005 97 | sensitivity: 1000 98 | snap: 0 99 | invert: 0 100 | type: 0 101 | axis: 0 102 | joyNum: 0 103 | - serializedVersion: 3 104 | m_Name: Mouse X 105 | descriptiveName: 106 | descriptiveNegativeName: 107 | negativeButton: 108 | positiveButton: 109 | altNegativeButton: 110 | altPositiveButton: 111 | gravity: 0 112 | dead: 0 113 | sensitivity: .100000001 114 | snap: 0 115 | invert: 0 116 | type: 1 117 | axis: 0 118 | joyNum: 0 119 | - serializedVersion: 3 120 | m_Name: Mouse Y 121 | descriptiveName: 122 | descriptiveNegativeName: 123 | negativeButton: 124 | positiveButton: 125 | altNegativeButton: 126 | altPositiveButton: 127 | gravity: 0 128 | dead: 0 129 | sensitivity: .100000001 130 | snap: 0 131 | invert: 0 132 | type: 1 133 | axis: 1 134 | joyNum: 0 135 | - serializedVersion: 3 136 | m_Name: Mouse ScrollWheel 137 | descriptiveName: 138 | descriptiveNegativeName: 139 | negativeButton: 140 | positiveButton: 141 | altNegativeButton: 142 | altPositiveButton: 143 | gravity: 0 144 | dead: 0 145 | sensitivity: .100000001 146 | snap: 0 147 | invert: 0 148 | type: 1 149 | axis: 2 150 | joyNum: 0 151 | - serializedVersion: 3 152 | m_Name: Horizontal 153 | descriptiveName: 154 | descriptiveNegativeName: 155 | negativeButton: 156 | positiveButton: 157 | altNegativeButton: 158 | altPositiveButton: 159 | gravity: 0 160 | dead: .189999998 161 | sensitivity: 1 162 | snap: 0 163 | invert: 0 164 | type: 2 165 | axis: 0 166 | joyNum: 0 167 | - serializedVersion: 3 168 | m_Name: Vertical 169 | descriptiveName: 170 | descriptiveNegativeName: 171 | negativeButton: 172 | positiveButton: 173 | altNegativeButton: 174 | altPositiveButton: 175 | gravity: 0 176 | dead: .189999998 177 | sensitivity: 1 178 | snap: 0 179 | invert: 1 180 | type: 2 181 | axis: 1 182 | joyNum: 0 183 | - serializedVersion: 3 184 | m_Name: Fire1 185 | descriptiveName: 186 | descriptiveNegativeName: 187 | negativeButton: 188 | positiveButton: joystick button 0 189 | altNegativeButton: 190 | altPositiveButton: 191 | gravity: 1000 192 | dead: .00100000005 193 | sensitivity: 1000 194 | snap: 0 195 | invert: 0 196 | type: 0 197 | axis: 0 198 | joyNum: 0 199 | - serializedVersion: 3 200 | m_Name: Fire2 201 | descriptiveName: 202 | descriptiveNegativeName: 203 | negativeButton: 204 | positiveButton: joystick button 1 205 | altNegativeButton: 206 | altPositiveButton: 207 | gravity: 1000 208 | dead: .00100000005 209 | sensitivity: 1000 210 | snap: 0 211 | invert: 0 212 | type: 0 213 | axis: 0 214 | joyNum: 0 215 | - serializedVersion: 3 216 | m_Name: Fire3 217 | descriptiveName: 218 | descriptiveNegativeName: 219 | negativeButton: 220 | positiveButton: joystick button 2 221 | altNegativeButton: 222 | altPositiveButton: 223 | gravity: 1000 224 | dead: .00100000005 225 | sensitivity: 1000 226 | snap: 0 227 | invert: 0 228 | type: 0 229 | axis: 0 230 | joyNum: 0 231 | - serializedVersion: 3 232 | m_Name: Jump 233 | descriptiveName: 234 | descriptiveNegativeName: 235 | negativeButton: 236 | positiveButton: joystick button 3 237 | altNegativeButton: 238 | altPositiveButton: 239 | gravity: 1000 240 | dead: .00100000005 241 | sensitivity: 1000 242 | snap: 0 243 | invert: 0 244 | type: 0 245 | axis: 0 246 | joyNum: 0 247 | -------------------------------------------------------------------------------- /ProjectSettings/NavMeshLayers.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!126 &1 4 | NavMeshLayers: 5 | m_ObjectHideFlags: 0 6 | Built-in Layer 0: 7 | name: Default 8 | cost: 1 9 | editType: 2 10 | Built-in Layer 1: 11 | name: Not Walkable 12 | cost: 1 13 | editType: 0 14 | Built-in Layer 2: 15 | name: Jump 16 | cost: 2 17 | editType: 2 18 | User Layer 0: 19 | name: 20 | cost: 1 21 | editType: 3 22 | User Layer 1: 23 | name: 24 | cost: 1 25 | editType: 3 26 | User Layer 2: 27 | name: 28 | cost: 1 29 | editType: 3 30 | User Layer 3: 31 | name: 32 | cost: 1 33 | editType: 3 34 | User Layer 4: 35 | name: 36 | cost: 1 37 | editType: 3 38 | User Layer 5: 39 | name: 40 | cost: 1 41 | editType: 3 42 | User Layer 6: 43 | name: 44 | cost: 1 45 | editType: 3 46 | User Layer 7: 47 | name: 48 | cost: 1 49 | editType: 3 50 | User Layer 8: 51 | name: 52 | cost: 1 53 | editType: 3 54 | User Layer 9: 55 | name: 56 | cost: 1 57 | editType: 3 58 | User Layer 10: 59 | name: 60 | cost: 1 61 | editType: 3 62 | User Layer 11: 63 | name: 64 | cost: 1 65 | editType: 3 66 | User Layer 12: 67 | name: 68 | cost: 1 69 | editType: 3 70 | User Layer 13: 71 | name: 72 | cost: 1 73 | editType: 3 74 | User Layer 14: 75 | name: 76 | cost: 1 77 | editType: 3 78 | User Layer 15: 79 | name: 80 | cost: 1 81 | editType: 3 82 | User Layer 16: 83 | name: 84 | cost: 1 85 | editType: 3 86 | User Layer 17: 87 | name: 88 | cost: 1 89 | editType: 3 90 | User Layer 18: 91 | name: 92 | cost: 1 93 | editType: 3 94 | User Layer 19: 95 | name: 96 | cost: 1 97 | editType: 3 98 | User Layer 20: 99 | name: 100 | cost: 1 101 | editType: 3 102 | User Layer 21: 103 | name: 104 | cost: 1 105 | editType: 3 106 | User Layer 22: 107 | name: 108 | cost: 1 109 | editType: 3 110 | User Layer 23: 111 | name: 112 | cost: 1 113 | editType: 3 114 | User Layer 24: 115 | name: 116 | cost: 1 117 | editType: 3 118 | User Layer 25: 119 | name: 120 | cost: 1 121 | editType: 3 122 | User Layer 26: 123 | name: 124 | cost: 1 125 | editType: 3 126 | User Layer 27: 127 | name: 128 | cost: 1 129 | editType: 3 130 | User Layer 28: 131 | name: 132 | cost: 1 133 | editType: 3 134 | -------------------------------------------------------------------------------- /ProjectSettings/NetworkManager.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!149 &1 4 | NetworkManager: 5 | m_ObjectHideFlags: 0 6 | m_DebugLevel: 0 7 | m_Sendrate: 15 8 | m_AssetToPrefab: {} 9 | -------------------------------------------------------------------------------- /ProjectSettings/Physics2DSettings.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!19 &1 4 | Physics2DSettings: 5 | m_ObjectHideFlags: 0 6 | m_Gravity: {x: 0, y: -9.81000042} 7 | m_DefaultMaterial: {fileID: 0} 8 | m_VelocityIterations: 8 9 | m_PositionIterations: 3 10 | m_VelocityThreshold: 1 11 | m_MaxLinearCorrection: .200000003 12 | m_MaxAngularCorrection: 8 13 | m_MaxTranslationSpeed: 100 14 | m_MaxRotationSpeed: 360 15 | m_BaumgarteScale: .200000003 16 | m_BaumgarteTimeOfImpactScale: .75 17 | m_TimeToSleep: .5 18 | m_LinearSleepTolerance: .00999999978 19 | m_AngularSleepTolerance: 2 20 | m_RaycastsHitTriggers: 1 21 | m_DeleteStopsCallbacks: 1 22 | m_LayerCollisionMatrix: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 23 | -------------------------------------------------------------------------------- /ProjectSettings/ProjectSettings.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!129 &1 4 | PlayerSettings: 5 | m_ObjectHideFlags: 0 6 | serializedVersion: 3 7 | AndroidProfiler: 0 8 | defaultScreenOrientation: 4 9 | targetDevice: 2 10 | targetGlesGraphics: 1 11 | targetResolution: 0 12 | accelerometerFrequency: 60 13 | companyName: DefaultCompany 14 | productName: UnityProjectionMapping 15 | defaultCursor: {fileID: 0} 16 | cursorHotspot: {x: 0, y: 0} 17 | defaultScreenWidth: 1024 18 | defaultScreenHeight: 768 19 | defaultScreenWidthWeb: 960 20 | defaultScreenHeightWeb: 600 21 | m_RenderingPath: 1 22 | m_MobileRenderingPath: 1 23 | m_ActiveColorSpace: 0 24 | m_MTRendering: 1 25 | m_MobileMTRendering: 0 26 | m_UseDX11: 1 27 | m_Stereoscopic3D: 0 28 | iosShowActivityIndicatorOnLoading: -1 29 | androidShowActivityIndicatorOnLoading: -1 30 | displayResolutionDialog: 1 31 | allowedAutorotateToPortrait: 1 32 | allowedAutorotateToPortraitUpsideDown: 1 33 | allowedAutorotateToLandscapeRight: 1 34 | allowedAutorotateToLandscapeLeft: 1 35 | useOSAutorotation: 1 36 | use32BitDisplayBuffer: 1 37 | use24BitDepthBuffer: 1 38 | defaultIsFullScreen: 1 39 | defaultIsNativeResolution: 1 40 | runInBackground: 0 41 | captureSingleScreen: 0 42 | Override IPod Music: 0 43 | Prepare IOS For Recording: 0 44 | enableHWStatistics: 1 45 | usePlayerLog: 1 46 | stripPhysics: 0 47 | forceSingleInstance: 0 48 | resizableWindow: 0 49 | useMacAppStoreValidation: 0 50 | gpuSkinning: 0 51 | xboxPIXTextureCapture: 0 52 | xboxEnableAvatar: 0 53 | xboxEnableKinect: 0 54 | xboxEnableKinectAutoTracking: 0 55 | xboxEnableFitness: 0 56 | visibleInBackground: 0 57 | macFullscreenMode: 2 58 | d3d9FullscreenMode: 1 59 | d3d11ForceExclusiveMode: 0 60 | xboxSpeechDB: 0 61 | xboxEnableHeadOrientation: 0 62 | xboxEnableGuest: 0 63 | videoMemoryForVertexBuffers: 0 64 | m_SupportedAspectRatios: 65 | 4:3: 1 66 | 5:4: 1 67 | 16:10: 1 68 | 16:9: 1 69 | Others: 1 70 | iPhoneBundleIdentifier: com.Company.ProductName 71 | metroEnableIndependentInputSource: 0 72 | metroEnableLowLatencyPresentationAPI: 0 73 | productGUID: ca799417bc134f64494c3b7d87581037 74 | iPhoneBundleVersion: 1.0 75 | AndroidBundleVersionCode: 1 76 | AndroidMinSdkVersion: 9 77 | AndroidPreferredInstallLocation: 1 78 | aotOptions: 79 | apiCompatibilityLevel: 2 80 | iPhoneStrippingLevel: 0 81 | iPhoneScriptCallOptimization: 0 82 | ForceInternetPermission: 0 83 | ForceSDCardPermission: 0 84 | CreateWallpaper: 0 85 | APKExpansionFiles: 0 86 | StripUnusedMeshComponents: 0 87 | iPhoneSdkVersion: 988 88 | iPhoneTargetOSVersion: 16 89 | uIPrerenderedIcon: 0 90 | uIRequiresPersistentWiFi: 0 91 | uIStatusBarHidden: 1 92 | uIExitOnSuspend: 0 93 | uIStatusBarStyle: 0 94 | iPhoneSplashScreen: {fileID: 0} 95 | iPhoneHighResSplashScreen: {fileID: 0} 96 | iPhoneTallHighResSplashScreen: {fileID: 0} 97 | iPhone47inSplashScreen: {fileID: 0} 98 | iPhone55inPortraitSplashScreen: {fileID: 0} 99 | iPhone55inLandscapeSplashScreen: {fileID: 0} 100 | iPadPortraitSplashScreen: {fileID: 0} 101 | iPadHighResPortraitSplashScreen: {fileID: 0} 102 | iPadLandscapeSplashScreen: {fileID: 0} 103 | iPadHighResLandscapeSplashScreen: {fileID: 0} 104 | AndroidTargetDevice: 0 105 | AndroidSplashScreenScale: 0 106 | AndroidKeystoreName: 107 | AndroidKeyaliasName: 108 | resolutionDialogBanner: {fileID: 0} 109 | m_BuildTargetIcons: [] 110 | m_BuildTargetBatching: [] 111 | webPlayerTemplate: APPLICATION:Default 112 | m_TemplateCustomTags: {} 113 | XboxTitleId: 114 | XboxImageXexPath: 115 | XboxSpaPath: 116 | XboxGenerateSpa: 0 117 | XboxDeployKinectResources: 0 118 | XboxSplashScreen: {fileID: 0} 119 | xboxEnableSpeech: 0 120 | xboxAdditionalTitleMemorySize: 0 121 | xboxDeployKinectHeadOrientation: 0 122 | xboxDeployKinectHeadPosition: 0 123 | ps3TitleConfigPath: 124 | ps3DLCConfigPath: 125 | ps3ThumbnailPath: 126 | ps3BackgroundPath: 127 | ps3SoundPath: 128 | ps3TrophyCommId: 129 | ps3NpCommunicationPassphrase: 130 | ps3TrophyPackagePath: 131 | ps3BootCheckMaxSaveGameSizeKB: 128 132 | ps3TrophyCommSig: 133 | ps3SaveGameSlots: 1 134 | ps3TrialMode: 0 135 | psp2Splashimage: {fileID: 0} 136 | psp2LiveAreaGate: {fileID: 0} 137 | psp2LiveAreaBackround: {fileID: 0} 138 | psp2NPTrophyPackPath: 139 | psp2NPCommsID: 140 | psp2NPCommsPassphrase: 141 | psp2NPCommsSig: 142 | psp2ParamSfxPath: 143 | psp2PackagePassword: 144 | psp2DLCConfigPath: 145 | psp2ThumbnailPath: 146 | psp2BackgroundPath: 147 | psp2SoundPath: 148 | psp2TrophyCommId: 149 | psp2TrophyPackagePath: 150 | psp2PackagedResourcesPath: 151 | flashStrippingLevel: 2 152 | spritePackerPolicy: 153 | scriptingDefineSymbols: {} 154 | metroPackageName: UnityProjectionMapping 155 | metroPackageLogo: 156 | metroPackageLogo140: 157 | metroPackageLogo180: 158 | metroPackageLogo240: 159 | metroPackageVersion: 160 | metroCertificatePath: 161 | metroCertificatePassword: 162 | metroCertificateSubject: 163 | metroCertificateIssuer: 164 | metroCertificateNotAfter: 0000000000000000 165 | metroApplicationDescription: UnityProjectionMapping 166 | metroStoreTileLogo80: 167 | metroStoreTileLogo: 168 | metroStoreTileLogo140: 169 | metroStoreTileLogo180: 170 | metroStoreTileWideLogo80: 171 | metroStoreTileWideLogo: 172 | metroStoreTileWideLogo140: 173 | metroStoreTileWideLogo180: 174 | metroStoreTileSmallLogo80: 175 | metroStoreTileSmallLogo: 176 | metroStoreTileSmallLogo140: 177 | metroStoreTileSmallLogo180: 178 | metroStoreSmallTile80: 179 | metroStoreSmallTile: 180 | metroStoreSmallTile140: 181 | metroStoreSmallTile180: 182 | metroStoreLargeTile80: 183 | metroStoreLargeTile: 184 | metroStoreLargeTile140: 185 | metroStoreLargeTile180: 186 | metroStoreSplashScreenImage: 187 | metroStoreSplashScreenImage140: 188 | metroStoreSplashScreenImage180: 189 | metroPhoneAppIcon: 190 | metroPhoneAppIcon140: 191 | metroPhoneAppIcon240: 192 | metroPhoneSmallTile: 193 | metroPhoneSmallTile140: 194 | metroPhoneSmallTile240: 195 | metroPhoneMediumTile: 196 | metroPhoneMediumTile140: 197 | metroPhoneMediumTile240: 198 | metroPhoneWideTile: 199 | metroPhoneWideTile140: 200 | metroPhoneWideTile240: 201 | metroPhoneSplashScreenImage: 202 | metroPhoneSplashScreenImage140: 203 | metroPhoneSplashScreenImage240: 204 | metroTileShortName: 205 | metroCommandLineArgsFile: 206 | metroTileShowName: 0 207 | metroMediumTileShowName: 0 208 | metroLargeTileShowName: 0 209 | metroWideTileShowName: 0 210 | metroDefaultTileSize: 1 211 | metroTileForegroundText: 1 212 | metroTileBackgroundColor: {r: 0, g: 0, b: 0, a: 1} 213 | metroSplashScreenBackgroundColor: {r: 0, g: 0, b: 0, a: 1} 214 | metroSplashScreenUseBackgroundColor: 0 215 | metroCapabilities: {} 216 | metroUnprocessedPlugins: [] 217 | metroCompilationOverrides: 1 218 | blackberryDeviceAddress: 219 | blackberryDevicePassword: 220 | blackberryTokenPath: 221 | blackberryTokenExires: 222 | blackberryTokenAuthor: 223 | blackberryTokenAuthorId: 224 | blackberryAuthorId: 225 | blackberryCskPassword: 226 | blackberrySaveLogPath: 227 | blackberryAuthorIdOveride: 0 228 | blackberrySharedPermissions: 0 229 | blackberryCameraPermissions: 0 230 | blackberryGPSPermissions: 0 231 | blackberryDeviceIDPermissions: 0 232 | blackberryMicrophonePermissions: 0 233 | blackberryGamepadSupport: 0 234 | blackberryBuildId: 0 235 | blackberryLandscapeSplashScreen: {fileID: 0} 236 | blackberryPortraitSplashScreen: {fileID: 0} 237 | blackberrySquareSplashScreen: {fileID: 0} 238 | tizenProductDescription: 239 | tizenProductURL: 240 | tizenCertificatePath: 241 | tizenCertificatePassword: 242 | tizenGPSPermissions: 0 243 | tizenMicrophonePermissions: 0 244 | stvDeviceAddress: 245 | firstStreamedLevelWithResources: 0 246 | unityRebuildLibraryVersion: 9 247 | unityForwardCompatibleVersion: 39 248 | unityStandardAssetsVersion: 0 249 | -------------------------------------------------------------------------------- /ProjectSettings/QualitySettings.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!47 &1 4 | QualitySettings: 5 | m_ObjectHideFlags: 0 6 | serializedVersion: 5 7 | m_CurrentQuality: 3 8 | m_QualitySettings: 9 | - serializedVersion: 2 10 | name: Fastest 11 | pixelLightCount: 0 12 | shadows: 0 13 | shadowResolution: 0 14 | shadowProjection: 1 15 | shadowCascades: 1 16 | shadowDistance: 15 17 | blendWeights: 1 18 | textureQuality: 1 19 | anisotropicTextures: 0 20 | antiAliasing: 0 21 | softParticles: 0 22 | softVegetation: 0 23 | vSyncCount: 0 24 | lodBias: .300000012 25 | maximumLODLevel: 0 26 | particleRaycastBudget: 4 27 | excludedTargetPlatforms: [] 28 | - serializedVersion: 2 29 | name: Fast 30 | pixelLightCount: 0 31 | shadows: 0 32 | shadowResolution: 0 33 | shadowProjection: 1 34 | shadowCascades: 1 35 | shadowDistance: 20 36 | blendWeights: 2 37 | textureQuality: 0 38 | anisotropicTextures: 0 39 | antiAliasing: 0 40 | softParticles: 0 41 | softVegetation: 0 42 | vSyncCount: 0 43 | lodBias: .400000006 44 | maximumLODLevel: 0 45 | particleRaycastBudget: 16 46 | excludedTargetPlatforms: [] 47 | - serializedVersion: 2 48 | name: Simple 49 | pixelLightCount: 1 50 | shadows: 1 51 | shadowResolution: 0 52 | shadowProjection: 1 53 | shadowCascades: 1 54 | shadowDistance: 20 55 | blendWeights: 2 56 | textureQuality: 0 57 | anisotropicTextures: 1 58 | antiAliasing: 0 59 | softParticles: 0 60 | softVegetation: 0 61 | vSyncCount: 0 62 | lodBias: .699999988 63 | maximumLODLevel: 0 64 | particleRaycastBudget: 64 65 | excludedTargetPlatforms: [] 66 | - serializedVersion: 2 67 | name: Good 68 | pixelLightCount: 2 69 | shadows: 2 70 | shadowResolution: 1 71 | shadowProjection: 1 72 | shadowCascades: 2 73 | shadowDistance: 40 74 | blendWeights: 2 75 | textureQuality: 0 76 | anisotropicTextures: 1 77 | antiAliasing: 0 78 | softParticles: 0 79 | softVegetation: 1 80 | vSyncCount: 1 81 | lodBias: 1 82 | maximumLODLevel: 0 83 | particleRaycastBudget: 256 84 | excludedTargetPlatforms: [] 85 | - serializedVersion: 2 86 | name: Beautiful 87 | pixelLightCount: 3 88 | shadows: 2 89 | shadowResolution: 2 90 | shadowProjection: 1 91 | shadowCascades: 2 92 | shadowDistance: 70 93 | blendWeights: 4 94 | textureQuality: 0 95 | anisotropicTextures: 2 96 | antiAliasing: 2 97 | softParticles: 1 98 | softVegetation: 1 99 | vSyncCount: 1 100 | lodBias: 1.5 101 | maximumLODLevel: 0 102 | particleRaycastBudget: 1024 103 | excludedTargetPlatforms: [] 104 | - serializedVersion: 2 105 | name: Fantastic 106 | pixelLightCount: 4 107 | shadows: 2 108 | shadowResolution: 2 109 | shadowProjection: 1 110 | shadowCascades: 4 111 | shadowDistance: 150 112 | blendWeights: 4 113 | textureQuality: 0 114 | anisotropicTextures: 2 115 | antiAliasing: 2 116 | softParticles: 1 117 | softVegetation: 1 118 | vSyncCount: 1 119 | lodBias: 2 120 | maximumLODLevel: 0 121 | particleRaycastBudget: 4096 122 | excludedTargetPlatforms: [] 123 | m_PerPlatformDefaultQuality: {} 124 | -------------------------------------------------------------------------------- /ProjectSettings/TagManager.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!78 &1 4 | TagManager: 5 | tags: 6 | - CalibrationSphere 7 | - 8 | Builtin Layer 0: Default 9 | Builtin Layer 1: TransparentFX 10 | Builtin Layer 2: Ignore Raycast 11 | Builtin Layer 3: 12 | Builtin Layer 4: Water 13 | Builtin Layer 5: UI 14 | Builtin Layer 6: 15 | Builtin Layer 7: 16 | User Layer 8: 17 | User Layer 9: 18 | User Layer 10: 19 | User Layer 11: 20 | User Layer 12: 21 | User Layer 13: 22 | User Layer 14: 23 | User Layer 15: 24 | User Layer 16: 25 | User Layer 17: 26 | User Layer 18: 27 | User Layer 19: 28 | User Layer 20: 29 | User Layer 21: 30 | User Layer 22: 31 | User Layer 23: 32 | User Layer 24: 33 | User Layer 25: 34 | User Layer 26: 35 | User Layer 27: 36 | User Layer 28: 37 | User Layer 29: 38 | User Layer 30: 39 | User Layer 31: 40 | m_SortingLayers: 41 | - name: Default 42 | userID: 0 43 | uniqueID: 0 44 | locked: 0 45 | -------------------------------------------------------------------------------- /ProjectSettings/TimeManager.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!5 &1 4 | TimeManager: 5 | m_ObjectHideFlags: 0 6 | Fixed Timestep: .0199999996 7 | Maximum Allowed Timestep: .333333343 8 | m_TimeScale: 1 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # UnityProjectionMapping 2 | 3 | Some images and an overview of the concept are available on my blog, http://blog.drewmacqu.com/2015/02/projection-mapping-in-unity.html 4 | 5 | ## Using the software 6 | 7 | ### Limitations 8 | 9 | This project uses OpenCV to calibrate the camera. The OpenCVSharp wrapper was used as it is compatible with Mono. This wrapper depends on specific OpenCV dlls. As a result this project is *Windows only* out of the box. However, only the CalibrateCamera function is used - it would be possible to rewrite this code section to allow use on other platforms. 10 | 11 | ### Prerequisites 12 | 13 | A 3D model of the projection surface is needed. It's important that the model be reasonably accurate as the calibration process can be a bit sensitive. 14 | 15 | OpenCV is used. The required DLLs are included in this project in the OpenCVDlls folder. These can be placed in the root folder of the Unity project you’re creating, or in a sensible place on your computer and added to the Windows path variable. 16 | 17 | You'll also need the VC11 32-bit redistributable, available from Microsoft: https://www.microsoft.com/en-us/download/confirmation.aspx?id=30679 18 | NB for me it automatically downloads the 64-bit version. You need the 32-bit one. Also on the Microsoft website you need to allow their scripts to run in Chrome - it just works in IE. 19 | 20 | ### Project setup 21 | 22 | Download and import the projection mapping package (UnityProjectionMapping.unitypackage in this repository). 23 | 24 | Create an empty GameObject and attach the CorrespondenceAcquisition.cs script to it. The script had two fields that need to be completed: 25 | 26 | 1. Drag your main camera onto the object's "Main camera" field in the Inspector tab. 27 | 2. Drag a crosshair image onto the object’s “Crosshair texture” field. A crosshair image is included in the package, it’s called crosshair.png 28 | 29 | Create a tag named “CalibrationSphere”. Unity packages do not export tags so it needs to be created manually. 30 | 31 | *Optional*: To help with locating your cursor in the projector screen, it’s useful to have full screen crosshairs. Due to Unity’s rendering pipeline it was necessary for the script that generates these lines to be attached to the main camera. Therefore, add the script DrawLine.cs to your main camera game object. 32 | 33 | ### Calibration 34 | 35 | The system needs to be calibrated so the virtual camera in Unity mimics the properties of the physical projector. This is achieved by defining correspondences between 3D points on the model and its respective point in the projected image. 36 | 37 | Start the game, either in the editor or as a standalone build. The game view needs to be being displayed by your projector onto the physical model. For ease I tend to have my desktop mirrored so I can use both the projector and the monitor for calibration. 38 | 39 | A correspondence between a 3D point and it’s projection on the 2D image plane is defined as follow: 40 | 41 | 1. Click a point on the 3D object. This will create a sphere at that point on the model and the screen will become partially opaque. *Tip*: if this doesn’t happen, your object is required to have a collider for ray casting to work. A mesh collider would be required for imported objects - less fine grained colliders would not work. 42 | 2. Looking at the image that’s being displayed by your projector. Click on where that newly created sphere is on your physical model. This will create a small crosshair marker at that point. These can be moved by hovering over them and dragging. 43 | 44 | This needs to be done at least seven times. After the seventh correspondence has been defined, the extrinsic and intrinsic matrices of the projector will be applied to the Unity camera. At this time the projection should be aligned to the physical object. More points can be added and existing points moved to achieve a better alignment. 45 | 46 | You can hide the calibration markers (spheres and crosshairs) by pressing the space bar. 47 | 48 | *Tips*: If you get exceptions (and the object disappears in the game view) after 7 points have been defined, something has gone wrong with the calibration. The calibration process is very sensitive to errors, and sometimes just fails. Try defining the points again, being very careful to avoid errors. Choose points that can be identified accurately on the model, such as corners. If you’ve tried it a few times with no luck, it’s likely your virtual model does not accurately reflect the physical model. Try measuring it up again and ensure all the dimensions are correct. Additionally, each point needs to contribute something to the optimisation (e.g. there needs to be linear independence between points). Try choosing points far apart that capture the full shape of the surface, not multiple points on the same plane. 49 | 50 | ### Saving and loading calibrations 51 | 52 | It can be time consuming to specify the correspondences needed to calibrate a projector. It is possible to save correspondences so you don't need to do it every time you restart the applications. 53 | 54 | To use the points recorder, create a new empty game object and add the PointsRecordReplay.cs script to it. In the Inspector tab, drag your CorrespondenceAcquisition object (whatever game object you attached the CorrespondenceAcquisition.cs script to) onto the "Points Holder" field. 55 | 56 | While the game is running, you can press "R" to record/save your points, and "P" to play/load your points. When playing in the editor, you can choose a different file name to store your points. In standalone mode, the file name "recording.xml" is used. 57 | 58 | ## Example unity project 59 | 60 | This repository contains an example Unity project that shows how the files need to be used (as described above). It contains a perfect cube object that could be mapped onto a physical cube. Seven good points for the cube would be the seven visible vertices. 61 | -------------------------------------------------------------------------------- /UnityProjectionMapping.unitypackage: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewmacquarrie/UnityProjectionMapping/118a247d1956621eab0d821f441ad98d10642612/UnityProjectionMapping.unitypackage --------------------------------------------------------------------------------