1 /*
2  * Copyright (C) 2010-2011 Dmitry Marakasov
3  *
4  * This file is part of glosm.
5  *
6  * glosm is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * glosm is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with glosm.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #include <glosm/FirstPersonViewer.hh>
21 
22 #include <glosm/Math.hh>
23 #include <glosm/Projection.hh>
24 #include <glosm/geomath.h>
25 
26 #include "mglu.h"
27 
28 #include "glosm/util/gl.h"
29 
30 #include <stdio.h>
31 
FirstPersonViewer()32 FirstPersonViewer::FirstPersonViewer(): pos_(), yaw_(0), pitch_(0), fov_(90.0), aspect_(1.0) {
33 }
34 
FirstPersonViewer(const Vector3i & pos)35 FirstPersonViewer::FirstPersonViewer(const Vector3i& pos): pos_(pos), yaw_(0), pitch_(0), fov_(90.0), aspect_(1.0) {
36 }
37 
FirstPersonViewer(const Vector3i & pos,float yaw,float pitch)38 FirstPersonViewer::FirstPersonViewer(const Vector3i& pos, float yaw, float pitch): pos_(pos), yaw_(yaw), pitch_(pitch), fov_(90.0), aspect_(1.0) {
39 }
40 
SetupViewerMatrix(const Projection & projection) const41 void FirstPersonViewer::SetupViewerMatrix(const Projection& projection) const {
42 	glMatrixMode(GL_PROJECTION);
43 	glLoadIdentity();
44 
45 	/* length of a meter in local units */
46 	float meterlen = projection.Project(pos_.Flattened() + Vector3i(0, 0, GEOM_UNITSINMETER), pos_.Flattened()).z;
47 
48 	/* viewer height in meters */
49 	float height = pos_.z / (float)GEOM_UNITSINMETER;
50 	if (height < 100.0f)
51 		height = 100.0f;
52 
53 	/* viewing distances is [1meter..100km] at under 100m height
54 	 * and increases linearly with going higher */
55 	float znear = 0.01f * height * meterlen;
56 	float zfar = 1000.0f * height * meterlen;
57 
58 	mgluPerspective(fov_ / M_PI * 180.0f, aspect_, znear, zfar);
59 
60 	glMatrixMode(GL_MODELVIEW);
61 	glLoadIdentity();
62 
63 	Vector3f dir = GetDirection();
64 	Vector3f up = Vector3f(0.0f, 0.0f, 1.0f);
65 
66 	mgluLookAt(0.0f, 0.0f, 0.0f, dir.x, dir.y, dir.z, up.x, up.y, up.z);
67 }
68 
GetPos(const Projection &) const69 Vector3i FirstPersonViewer::GetPos(const Projection& /* unused*/) const {
70 	return pos_;
71 }
72 
SetFov(float fov)73 void FirstPersonViewer::SetFov(float fov) {
74 	fov_ = fov;
75 }
76 
SetAspect(float aspect)77 void FirstPersonViewer::SetAspect(float aspect) {
78 	aspect_ = aspect;
79 }
80 
GetDirection() const81 Vector3f FirstPersonViewer::GetDirection() const {
82 	return Vector3f::FromYawPitch(yaw_, pitch_);
83 }
84 
SetPos(Vector3i pos)85 void FirstPersonViewer::SetPos(Vector3i pos) {
86 	pos_ = pos;
87 }
88 
Move(int flags,float speed,float time)89 void FirstPersonViewer::Move(int flags, float speed, float time) {
90 	/* 1 meter direction-collinear vector in OSM coordinate space */
91 	Vector3f dirbasis = Vector3f(
92 			GEOM_LONSPAN / WGS84_EARTH_EQ_LENGTH / cos(pos_.y * GEOM_DEG_TO_RAD),
93 			GEOM_LONSPAN / WGS84_EARTH_EQ_LENGTH,
94 			GEOM_UNITSINMETER
95 		);
96 
97 	Vector3f dir = GetDirection();
98 	Vector3f worldup = Vector3f(0.0f, 0.0f, 1.0f);
99 	Vector3f right = dir.CrossProduct(worldup).Normalized();
100 	Vector3f relup = right.CrossProduct(dir).Normalized();
101 
102 	if (flags & FORWARD)
103 		pos_ += dirbasis * dir * speed * time;
104 	if (flags & BACKWARD)
105 		pos_ -= dirbasis * dir * speed * time;
106 	if (flags & LEFT)
107 		pos_ -= dirbasis * right * speed * time;
108 	if (flags & RIGHT)
109 		pos_ += dirbasis * right * speed * time;
110 	if (flags & UP)
111 		pos_ += dirbasis * relup * speed * time;
112 	if (flags & DOWN)
113 		pos_ -= dirbasis * relup * speed * time;
114 	if (flags & HIGHER)
115 		pos_ += dirbasis * worldup * speed * time;
116 	if (flags & LOWER)
117 		pos_ -= dirbasis * worldup * speed * time;
118 
119 	/* Wrap around */
120 	if (pos_.x > GEOM_MAXLON)
121 		pos_.x -= GEOM_LONSPAN;
122 	if (pos_.x < GEOM_MINLON)
123 		pos_.x += GEOM_LONSPAN;
124 
125 	/* Limit poles */
126 	if (pos_.y > GEOM_MERCATOR_MAXLAT)
127 		pos_.y = GEOM_MERCATOR_MAXLAT;
128 	if (pos_.y < GEOM_MERCATOR_MINLAT)
129 		pos_.y = GEOM_MERCATOR_MINLAT;
130 
131 	/* Limit height */
132 	if (pos_.z < 0.0)
133 		pos_.z = 0.0;
134 	if (pos_.z > std::numeric_limits<osmint_t>::max())
135 		pos_.z = std::numeric_limits<osmint_t>::max();
136 }
137 
FixRotation()138 void FirstPersonViewer::FixRotation() {
139 	static const float PitchLimit = M_PI/2.0*0.9;
140 
141 	if (pitch_ > PitchLimit)
142 		pitch_ = PitchLimit;
143 	if (pitch_ < -PitchLimit)
144 		pitch_ = -PitchLimit;
145 	if (yaw_ > M_PI)
146 		yaw_ -= M_PI*2.0;
147 	if (yaw_ < -M_PI)
148 		yaw_ += M_PI*2.0;
149 }
150 
SetRotation(float yaw,float pitch)151 void FirstPersonViewer::SetRotation(float yaw, float pitch) {
152 	yaw_ = yaw;
153 	pitch_ = pitch;
154 
155 	FixRotation();
156 }
157 
Rotate(float yawspeed,float pitchspeed,float time)158 void FirstPersonViewer::Rotate(float yawspeed, float pitchspeed, float time) {
159 	yaw_ += yawspeed * time;
160 	pitch_ += pitchspeed * time;
161 
162 	FixRotation();
163 }
164 
GetYaw() const165 float FirstPersonViewer::GetYaw() const {
166 	return yaw_;
167 }
168 
GetPitch() const169 float FirstPersonViewer::GetPitch() const {
170 	return pitch_;
171 }
172 
GetFov() const173 float FirstPersonViewer::GetFov() const {
174 	return fov_;
175 }
176 
GetAspect() const177 float FirstPersonViewer::GetAspect() const {
178 	return aspect_;
179 }
180 
MutablePos()181 Vector3d& FirstPersonViewer::MutablePos() {
182 	return pos_;
183 }
184