History

Various fixes, removed the bulk of main.cpp.

Andrzej Kapolka committed 13 years ago

Commit 1e993891 parent 75eee4b5 files · +56 −2165Browse files

Showing the changes to interface/src/main.cpp (1 of 5 files). Show all

Unified · Split

interface/src/main.cpp+1−2153
@@ -15,2040 +15,8 @@ · 14 unmodified lines
15// Welcome Aboard!15// Welcome Aboard!
16//16//
17 17
18#include <math.h>
19#include <string.h>
20#include <sstream>
21#include <stdio.h>
22#include <stdlib.h>
23
24#ifdef _WIN32
25#include "Syssocket.h"
26#include "Systime.h"
27#else
28#include <sys/time.h>
29#include <arpa/inet.h>
30#include <ifaddrs.h>
31#endif
32
33#include <pthread.h>
34
35#include <glm/glm.hpp>
36#include <glm/gtc/matrix_transform.hpp>
37#include <glm/gtc/type_ptr.hpp>
38
39#include "InterfaceConfig.h"
40
41#include "Log.h"
42#include "shared_Log.h"
43#include "voxels_Log.h"
44#include "avatars_Log.h"
45
46#include "world.h"
47#include "Util.h"
48#ifndef _WIN32
49#include "Audio.h"
50#endif
51
52#include "AngleUtil.h"
53#include "Stars.h"
54
55#include "ui/ChatEntry.h"
56#include "ui/MenuRow.h"
57#include "ui/MenuColumn.h"
58#include "ui/Menu.h"
59#include "ui/TextRenderer.h"
60#include "renderer/ProgramObject.h"
61#include "renderer/ShaderObject.h"
62
63#include "Application.h"18#include "Application.h"
64#include "Camera.h"19#include "Log.h"
65#include "Avatar.h"
66#include <AgentList.h>
67#include <AgentTypes.h>
68#include "VoxelSystem.h"
69#include "Environment.h"
70#include "Oscilloscope.h"
71#include "UDPSocket.h"
72#include "SerialInterface.h"
73#include <PacketHeaders.h>
74#include <AvatarData.h>
75#include <PerfStat.h>
76#include <SimpleMovingAverage.h>
77
78#include "ViewFrustum.h"
79#include "HandControl.h"
80#include "AvatarRenderer.h"
81#include "OculusManager.h"
82
83using namespace std;
84
85void reshape(int width, int height); // will be defined below
86void loadViewFrustum(ViewFrustum& viewFrustum); // will be defined below
87
88glm::vec3 getGravity(glm::vec3 pos); //get the local gravity vector at this location in the universe
89
90bool enableNetworkThread = true;
91pthread_t networkReceiveThread;
92bool stopNetworkReceiveThread = false;
93
94unsigned char incomingPacket[MAX_PACKET_SIZE];
95int packetCount = 0;
96int packetsPerSecond = 0;
97int bytesPerSecond = 0;
98int bytesCount = 0;
99
100
101int screenWidth = 1200; // Window size
102int screenHeight = 800;
103
104int fullscreen = 0;
105float aspectRatio = 1.0f;
106
107// PER: Jeffrey - please move these our of main.cpp - also these not constants!
108float mouseViewShiftYaw = 0.0f;
109float mouseViewShiftPitch = 0.0f;
110bool USING_MOUSE_VIEW_SHIFT = false;
111float MOUSE_VIEW_SHIFT_RATE = 40.0f;
112float MOUSE_VIEW_SHIFT_YAW_MARGIN = (float)(::screenWidth * 0.2f);
113float MOUSE_VIEW_SHIFT_PITCH_MARGIN = (float)(::screenHeight * 0.2f);
114float MOUSE_VIEW_SHIFT_YAW_LIMIT = 45.0;
115float MOUSE_VIEW_SHIFT_PITCH_LIMIT = 30.0;
116
117//CameraMode defaultCameraMode = CAMERA_MODE_FIRST_PERSON;
118CameraMode defaultCameraMode = CAMERA_MODE_THIRD_PERSON;
119
120bool wantColorRandomizer = true; // for addSphere and load file
121
122Oscilloscope audioScope(256,200,true);
123
124ViewFrustum viewFrustum; // current state of view frustum, perspective, orientation, etc.
125
126Avatar myAvatar(true); // The rendered avatar of oneself
127Camera myCamera; // My view onto the world
128Camera viewFrustumOffsetCamera; // The camera we use to sometimes show the view frustum from an offset mode
129
130AvatarRenderer avatarRenderer;
131
132// Starfield information
133char starFile[] = "https://s3-us-west-1.amazonaws.com/highfidelity/stars.txt";
134char starCacheFile[] = "cachedStars.txt";
135Stars stars;
136
137glm::vec3 box(WORLD_SIZE,WORLD_SIZE,WORLD_SIZE);
138
139VoxelSystem voxels;
140
141Environment environment;
142
143
144#ifndef _WIN32
145Audio audio(&audioScope, &myAvatar);
146#endif
147
148#define IDLE_SIMULATE_MSECS 16 // How often should call simulate and other stuff
149 // in the idle loop? (60 FPS is default)
150
151
152glm::vec3 start_location(6.1f, 0, 1.4f); // Where one's own agent begins in the world
153 // (will be overwritten if avatar data file is found)
154
155bool renderWarningsOn = false; // Whether to show render pipeline warnings
156bool renderStatsOn = false; // Whether to show onscreen text overlay with stats
157bool renderVoxels = true; // Whether to render voxels
158bool renderStarsOn = true; // Whether to display the stars
159bool renderAtmosphereOn = true; // Whether to display the atmosphere
160bool renderAvatarsOn = true; // Whether to render avatars
161bool renderFirstPersonOn = false; // Whether to render in first person
162bool paintOn = false; // Whether to paint voxels as you fly around
163VoxelDetail paintingVoxel; // The voxel we're painting if we're painting
164unsigned char dominantColor = 0; // The dominant color of the voxel we're painting
165bool perfStatsOn = false; // Do we want to display perfStats?
166bool wantMonochrome = false; // ask server to send us in monochrome
167bool wantResIn = false; // ask server to res in
168
169bool logOn = true; // Whether to show on-screen log
170
171bool wantToKillLocalVoxels = false;
172
173int noiseOn = 0; // Whether to add random noise
174float noise = 1.0; // Overall magnitude scaling for random noise levels
175
176bool gyroLook = true; // Whether to allow the gyro data from head to move your view
177
178int displayLevels = 0;
179bool lookingInMirror = 0; // Are we currently rendering one's own head as if in mirror?
180
181int displayHeadMouse = 1; // Display sample mouse pointer controlled by head movement
182int headMouseX, headMouseY;
183
184HandControl handControl;
185
186int mouseX = 0;
187int mouseY = 0;
188
189// Mouse location at start of last down click
190int mousePressed = 0; // true if mouse has been pressed (clear when finished)
191
192// The current mode for mouse interaction
193enum MouseMode { NO_EDIT_MODE, ADD_VOXEL_MODE, DELETE_VOXEL_MODE, COLOR_VOXEL_MODE };
194MouseMode mouseMode = NO_EDIT_MODE;
195VoxelDetail mouseVoxel; // details of the voxel under the mouse cursor
196float mouseVoxelScale = 1.0f / 1024.0f; // the scale for adding/removing voxels
197
198Menu menu; // main menu
199int menuOn = 1; // Whether to show onscreen menu
200
201ChatEntry chatEntry; // chat entry field
202bool chatEntryOn = false; // Whether to show the chat entry
203
204bool oculusOn = false; // Whether to configure the display for the Oculus Rift
205GLuint oculusTextureID = 0; // The texture to which we render for Oculus distortion
206ProgramObject* oculusProgram = 0; // The GLSL program containing the distortion shader
207float oculusDistortionScale = 1.25; // Controls the Oculus field of viewa
208
209SerialInterface serialPort;
210
211glm::vec3 gravity;
212
213// Frame Rate Measurement
214
215int frameCount = 0;
216float FPS = 120.f;
217timeval timerStart, timerEnd;
218timeval lastTimeIdle;
219double elapsedTime;
220timeval applicationStartupTime;
221bool justStarted = true;
222
223// Every second, check the frame rates and other stuff
224void Timer(int extra) {
225 gettimeofday(&timerEnd, NULL);
226 FPS = (float)frameCount / ((float)diffclock(&timerStart, &timerEnd) / 1000.f);
227 packetsPerSecond = (float)packetCount / ((float)diffclock(&timerStart, &timerEnd) / 1000.f);
228 bytesPerSecond = (float)bytesCount / ((float)diffclock(&timerStart, &timerEnd) / 1000.f);
229 frameCount = 0;
230 packetCount = 0;
231 bytesCount = 0;
232
233 glutTimerFunc(1000,Timer,0);
234 gettimeofday(&timerStart, NULL);
235
236 // if we haven't detected gyros, check for them now
237 if (!serialPort.active) {
238 serialPort.pair();
239 }
240}
241
242void displayStats(void) {
243 int statsVerticalOffset = 50;
244 if (::menuOn == 0) {
245 statsVerticalOffset = 8;
246 }
247
248 char stats[200];
249 sprintf(stats, "%3.0f FPS, %d Pkts/sec, %3.2f Mbps",
250 FPS, packetsPerSecond, (float)bytesPerSecond * 8.f / 1000000.f);
251 drawtext(10, statsVerticalOffset + 15, 0.10f, 0, 1.0, 0, stats);
252
253 std::stringstream voxelStats;
254 voxelStats.precision(4);
255 voxelStats << "Voxels Rendered: " << voxels.getVoxelsRendered() / 1000.f << "K Updated: " << voxels.getVoxelsUpdated()/1000.f << "K";
256 drawtext(10, statsVerticalOffset + 230, 0.10f, 0, 1.0, 0, (char *)voxelStats.str().c_str());
257
258 voxelStats.str("");
259 voxelStats << "Voxels Created: " << voxels.getVoxelsCreated() / 1000.f << "K (" << voxels.getVoxelsCreatedPerSecondAverage() / 1000.f
260 << "Kps) ";
261 drawtext(10, statsVerticalOffset + 250, 0.10f, 0, 1.0, 0, (char *)voxelStats.str().c_str());
262
263 voxelStats.str("");
264 voxelStats << "Voxels Colored: " << voxels.getVoxelsColored() / 1000.f << "K (" << voxels.getVoxelsColoredPerSecondAverage() / 1000.f
265 << "Kps) ";
266 drawtext(10, statsVerticalOffset + 270, 0.10f, 0, 1.0, 0, (char *)voxelStats.str().c_str());
267
268 voxelStats.str("");
269 voxelStats << "Voxel Bits Read: " << voxels.getVoxelsBytesRead() * 8.f / 1000000.f
270 << "M (" << voxels.getVoxelsBytesReadPerSecondAverage() * 8.f / 1000000.f << " Mbps)";
271 drawtext(10, statsVerticalOffset + 290,0.10f, 0, 1.0, 0, (char *)voxelStats.str().c_str());
272
273 voxelStats.str("");
274 float voxelsBytesPerColored = voxels.getVoxelsColored()
275 ? ((float) voxels.getVoxelsBytesRead() / voxels.getVoxelsColored())
276 : 0;
277
278 voxelStats << "Voxels Bits per Colored: " << voxelsBytesPerColored * 8;
279 drawtext(10, statsVerticalOffset + 310, 0.10f, 0, 1.0, 0, (char *)voxelStats.str().c_str());
280
281 Agent *avatarMixer = AgentList::getInstance()->soloAgentOfType(AGENT_TYPE_AVATAR_MIXER);
282 char avatarMixerStats[200];
283
284 if (avatarMixer) {
285 sprintf(avatarMixerStats, "Avatar Mixer: %.f kbps, %.f pps",
286 roundf(avatarMixer->getAverageKilobitsPerSecond()),
287 roundf(avatarMixer->getAveragePacketsPerSecond()));
288 } else {
289 sprintf(avatarMixerStats, "No Avatar Mixer");
290 }
291
292 drawtext(10, statsVerticalOffset + 330, 0.10f, 0, 1.0, 0, avatarMixerStats);
293
294 if (::perfStatsOn) {
295 // Get the PerfStats group details. We need to allocate and array of char* long enough to hold 1+groups
296 char** perfStatLinesArray = new char*[PerfStat::getGroupCount()+1];
297 int lines = PerfStat::DumpStats(perfStatLinesArray);
298 int atZ = 150; // arbitrary place on screen that looks good
299 for (int line=0; line < lines; line++) {
300 drawtext(10, statsVerticalOffset + atZ, 0.10f, 0, 1.0, 0, perfStatLinesArray[line]);
301 delete perfStatLinesArray[line]; // we're responsible for cleanup
302 perfStatLinesArray[line]=NULL;
303 atZ+=20; // height of a line
304 }
305 delete []perfStatLinesArray; // we're responsible for cleanup
306 }
307}
308
309void initDisplay(void) {
310 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
311 glEnable(GL_BLEND);
312 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
313 glShadeModel (GL_SMOOTH);
314 glEnable(GL_LIGHTING);
315 glEnable(GL_LIGHT0);
316 glEnable(GL_DEPTH_TEST);
317
318 if (fullscreen) glutFullScreen();
319}
320
321void init(void) {
322 voxels.init();
323 voxels.setViewerAvatar(&myAvatar);
324 voxels.setCamera(&myCamera);
325
326 environment.init();
327
328 handControl.setScreenDimensions(::screenWidth, ::screenHeight);
329
330 headMouseX = ::screenWidth /2;
331 headMouseY = ::screenHeight/2;
332
333 stars.readInput(starFile, starCacheFile, 0);
334
335 if (noiseOn) {
336 myAvatar.setNoise(noise);
337 }
338
339 myAvatar.setPosition(start_location);
340 myCamera.setMode(defaultCameraMode);
341
342 OculusManager::connect();
343
344 gettimeofday(&timerStart, NULL);
345 gettimeofday(&lastTimeIdle, NULL);
346}
347
348void terminate () {
349 // Close serial port
350 // close(serial_fd);
351
352 myAvatar.writeAvatarDataToFile();
353
354 #ifndef _WIN32
355 audio.terminate();
356 #endif
357
358 if (enableNetworkThread) {
359 stopNetworkReceiveThread = true;
360 pthread_join(networkReceiveThread, NULL);
361 }
362
363 exit(EXIT_SUCCESS);
364}
365
366void reset_sensors() {
367
368 myAvatar.setPosition(start_location);
369 headMouseX = ::screenWidth / 2;
370 headMouseY = ::screenHeight / 2;
371
372 myAvatar.reset();
373}
374
375void sendVoxelEditMessage(PACKET_HEADER header, VoxelDetail& detail) {
376 unsigned char* bufferOut;
377 int sizeOut;
378
379 if (createVoxelEditMessage(header, 0, 1, &detail, bufferOut, sizeOut)){
380 AgentList::getInstance()->broadcastToAgents(bufferOut, sizeOut, &AGENT_TYPE_VOXEL, 1);
381 delete bufferOut;
382 }
383}
384
385// Using gyro data, update both view frustum and avatar head position
386void updateAvatar(float deltaTime) {
387
388 // Update my avatar's head position from gyros
389 myAvatar.updateHeadFromGyros(deltaTime, &serialPort, &gravity);
390
391 // Grab latest readings from the gyros
392 float measuredPitchRate = serialPort.getLastPitchRate();
393 float measuredYawRate = serialPort.getLastYawRate();
394
395 // Update gyro-based mouse (X,Y on screen)
396 const float MIN_MOUSE_RATE = 1.0;
397 const float HORIZONTAL_PIXELS_PER_DEGREE = 2880.f / 45.f;
398 const float VERTICAL_PIXELS_PER_DEGREE = 1800.f / 30.f;
399 if (powf(measuredYawRate * measuredYawRate +
400 measuredPitchRate * measuredPitchRate, 0.5) > MIN_MOUSE_RATE)
401 {
402 headMouseX += measuredYawRate * HORIZONTAL_PIXELS_PER_DEGREE * deltaTime;
403 headMouseY -= measuredPitchRate * VERTICAL_PIXELS_PER_DEGREE * deltaTime;
404 }
405 headMouseX = max(headMouseX, 0);
406 headMouseX = min(headMouseX, ::screenWidth);
407 headMouseY = max(headMouseY, 0);
408 headMouseY = min(headMouseY, ::screenHeight);
409
410 // Update head and body pitch and yaw based on measured gyro rates
411 if (::gyroLook) {
412 // Render Yaw
413 float renderYawSpring = fabs(headMouseX - ::screenWidth / 2.f) / (::screenWidth / 2.f);
414 const float RENDER_YAW_MULTIPLY = 4.f;
415 myAvatar.setRenderYaw((1.f - renderYawSpring * deltaTime) * myAvatar.getRenderYaw() +
416 renderYawSpring * deltaTime * -myAvatar.getHeadYaw() * RENDER_YAW_MULTIPLY);
417 // Render Pitch
418 float renderPitchSpring = fabs(headMouseY - ::screenHeight / 2.f) / (::screenHeight / 2.f);
419 const float RENDER_PITCH_MULTIPLY = 4.f;
420 myAvatar.setRenderPitch((1.f - renderPitchSpring * deltaTime) * myAvatar.getRenderPitch() +
421 renderPitchSpring * deltaTime * -myAvatar.getHeadPitch() * RENDER_PITCH_MULTIPLY);
422 }
423
424
425 if (USING_MOUSE_VIEW_SHIFT)
426 {
427 //make it so that when your mouse hits the edge of the screen, the camera shifts
428 float rightBoundary = (float)::screenWidth - MOUSE_VIEW_SHIFT_YAW_MARGIN;
429 float bottomBoundary = (float)::screenHeight - MOUSE_VIEW_SHIFT_PITCH_MARGIN;
430
431 if (mouseX > rightBoundary) {
432 float f = (mouseX - rightBoundary) / ( (float)::screenWidth - rightBoundary);
433 mouseViewShiftYaw += MOUSE_VIEW_SHIFT_RATE * f * deltaTime;
434 if (mouseViewShiftYaw > MOUSE_VIEW_SHIFT_YAW_LIMIT) { mouseViewShiftYaw = MOUSE_VIEW_SHIFT_YAW_LIMIT; }
435 } else if (mouseX < MOUSE_VIEW_SHIFT_YAW_MARGIN) {
436 float f = 1.0 - (mouseX / MOUSE_VIEW_SHIFT_YAW_MARGIN);
437 mouseViewShiftYaw -= MOUSE_VIEW_SHIFT_RATE * f * deltaTime;
438 if (mouseViewShiftYaw < -MOUSE_VIEW_SHIFT_YAW_LIMIT) { mouseViewShiftYaw = -MOUSE_VIEW_SHIFT_YAW_LIMIT; }
439 }
440 if (mouseY < MOUSE_VIEW_SHIFT_PITCH_MARGIN) {
441 float f = 1.0 - (mouseY / MOUSE_VIEW_SHIFT_PITCH_MARGIN);
442 mouseViewShiftPitch += MOUSE_VIEW_SHIFT_RATE * f * deltaTime;
443 if ( mouseViewShiftPitch > MOUSE_VIEW_SHIFT_PITCH_LIMIT ) { mouseViewShiftPitch = MOUSE_VIEW_SHIFT_PITCH_LIMIT; }
444 }
445 else if (mouseY > bottomBoundary) {
446 float f = (mouseY - bottomBoundary) / ((float)::screenHeight - bottomBoundary);
447 mouseViewShiftPitch -= MOUSE_VIEW_SHIFT_RATE * f * deltaTime;
448 if (mouseViewShiftPitch < -MOUSE_VIEW_SHIFT_PITCH_LIMIT) { mouseViewShiftPitch = -MOUSE_VIEW_SHIFT_PITCH_LIMIT; }
449 }
450 }
451
452 if (OculusManager::isConnected()) {
453 float yaw, pitch, roll;
454 OculusManager::getEulerAngles(yaw, pitch, roll);
455
456 myAvatar.setHeadYaw(-yaw);
457 myAvatar.setHeadPitch(pitch);
458 myAvatar.setHeadRoll(roll);
459 }
460
461 // Get audio loudness data from audio input device
462 #ifndef _WIN32
463 myAvatar.setLoudness(audio.getInputLoudness());
464 #endif
465
466 // Update Avatar with latest camera and view frustum data...
467 // NOTE: we get this from the view frustum, to make it simpler, since the
468 // loadViewFrumstum() method will get the correct details from the camera
469 // We could optimize this to not actually load the viewFrustum, since we don't
470 // actually need to calculate the view frustum planes to send these details
471 // to the server.
472 loadViewFrustum(::viewFrustum);
473 myAvatar.setCameraPosition(::viewFrustum.getPosition());
474 myAvatar.setCameraDirection(::viewFrustum.getDirection());
475 myAvatar.setCameraUp(::viewFrustum.getUp());
476 myAvatar.setCameraRight(::viewFrustum.getRight());
477 myAvatar.setCameraFov(::viewFrustum.getFieldOfView());
478 myAvatar.setCameraAspectRatio(::viewFrustum.getAspectRatio());
479 myAvatar.setCameraNearClip(::viewFrustum.getNearClip());
480 myAvatar.setCameraFarClip(::viewFrustum.getFarClip());
481
482 AgentList* agentList = AgentList::getInstance();
483 if (agentList->getOwnerID() != UNKNOWN_AGENT_ID) {
484 // if I know my ID, send head/hand data to the avatar mixer and voxel server
485 unsigned char broadcastString[200];
486 unsigned char* endOfBroadcastStringWrite = broadcastString;
487
488 *(endOfBroadcastStringWrite++) = PACKET_HEADER_HEAD_DATA;
489 endOfBroadcastStringWrite += packAgentId(endOfBroadcastStringWrite, agentList->getOwnerID());
490
491 endOfBroadcastStringWrite += myAvatar.getBroadcastData(endOfBroadcastStringWrite);
492
493 const char broadcastReceivers[2] = {AGENT_TYPE_VOXEL, AGENT_TYPE_AVATAR_MIXER};
494 AgentList::getInstance()->broadcastToAgents(broadcastString, endOfBroadcastStringWrite - broadcastString, broadcastReceivers, sizeof(broadcastReceivers));
495 }
496
497 // If I'm in paint mode, send a voxel out to VOXEL server agents.
498 if (::paintOn) {
499
500 glm::vec3 avatarPos = myAvatar.getPosition();
501
502 // For some reason, we don't want to flip X and Z here.
503 ::paintingVoxel.x = avatarPos.x / 10.0;
504 ::paintingVoxel.y = avatarPos.y / 10.0;
505 ::paintingVoxel.z = avatarPos.z / 10.0;
506
507 if (::paintingVoxel.x >= 0.0 && ::paintingVoxel.x <= 1.0 &&
508 ::paintingVoxel.y >= 0.0 && ::paintingVoxel.y <= 1.0 &&
509 ::paintingVoxel.z >= 0.0 && ::paintingVoxel.z <= 1.0) {
510
511 sendVoxelEditMessage(PACKET_HEADER_SET_VOXEL, ::paintingVoxel);
512 }
513 }
514}
515
516/////////////////////////////////////////////////////////////////////////////////////
517// loadViewFrustum()
518//
519// Description: this will load the view frustum bounds for EITHER the head
520// or the "myCamera".
521//
522
523// These global scoped variables are used by our loadViewFrustum() and renderViewFrustum functions below, but are also
524// available as globals so that the keyboard and menu can manipulate them.
525
526bool frustumOn = false; // Whether or not to display the debug view frustum
527bool cameraFrustum = true; // which frustum to look at
528
529bool viewFrustumFromOffset =false; // Wether or not to offset the view of the frustum
530float viewFrustumOffsetYaw = -135.0; // the following variables control yaw, pitch, roll and distance form regular
531float viewFrustumOffsetPitch = 0.0; // camera to the offset camera
532float viewFrustumOffsetRoll = 0.0;
533float viewFrustumOffsetDistance = 25.0;
534float viewFrustumOffsetUp = 0.0;
535
536void loadViewFrustum(ViewFrustum& viewFrustum) {
537 // We will use these below, from either the camera or head vectors calculated above
538 glm::vec3 position;
539 glm::vec3 direction;
540 glm::vec3 up;
541 glm::vec3 right;
542 float fov, nearClip, farClip;
543
544 // Camera or Head?
545 if (::cameraFrustum) {
546 position = ::myCamera.getPosition();
547 } else {
548 position = ::myAvatar.getHeadPosition();
549 }
550
551 fov = ::myCamera.getFieldOfView();
552 nearClip = ::myCamera.getNearClip();
553 farClip = ::myCamera.getFarClip();
554
555 Orientation o = ::myCamera.getOrientation();
556
557 direction = o.getFront();
558 up = o.getUp();
559 right = o.getRight();
560
561 /*
562 printf("position.x=%f, position.y=%f, position.z=%f\n", position.x, position.y, position.z);
563 printf("yaw=%f, pitch=%f, roll=%f\n", yaw,pitch,roll);
564 printf("direction.x=%f, direction.y=%f, direction.z=%f\n", direction.x, direction.y, direction.z);
565 printf("up.x=%f, up.y=%f, up.z=%f\n", up.x, up.y, up.z);
566 printf("right.x=%f, right.y=%f, right.z=%f\n", right.x, right.y, right.z);
567 printf("fov=%f\n", fov);
568 printf("nearClip=%f\n", nearClip);
569 printf("farClip=%f\n", farClip);
570 */
571
572 // Set the viewFrustum up with the correct position and orientation of the camera
573 viewFrustum.setPosition(position);
574 viewFrustum.setOrientation(direction,up,right);
575
576 // Also make sure it's got the correct lens details from the camera
577 viewFrustum.setFieldOfView(fov);
578 viewFrustum.setNearClip(nearClip);
579 viewFrustum.setFarClip(farClip);
580
581 // Ask the ViewFrustum class to calculate our corners
582 viewFrustum.calculate();
583}
584
585/////////////////////////////////////////////////////////////////////////////////////
586// renderViewFrustum()
587//
588// Description: this will render the view frustum bounds for EITHER the head
589// or the "myCamera".
590//
591// Frustum rendering mode. For debug purposes, we allow drawing the frustum in a couple of different ways.
592// We can draw it with each of these parts:
593// * Origin Direction/Up/Right vectors - these will be drawn at the point of the camera
594// * Near plane - this plane is drawn very close to the origin point.
595// * Right/Left planes - these two planes are drawn between the near and far planes.
596// * Far plane - the plane is drawn in the distance.
597// Modes - the following modes, will draw the following parts.
598// * All - draws all the parts listed above
599// * Planes - draws the planes but not the origin vectors
600// * Origin Vectors - draws the origin vectors ONLY
601// * Near Plane - draws only the near plane
602// * Far Plane - draws only the far plane
603#define FRUSTUM_DRAW_MODE_ALL 0
604#define FRUSTUM_DRAW_MODE_VECTORS 1
605#define FRUSTUM_DRAW_MODE_PLANES 2
606#define FRUSTUM_DRAW_MODE_NEAR_PLANE 3
607#define FRUSTUM_DRAW_MODE_FAR_PLANE 4
608#define FRUSTUM_DRAW_MODE_COUNT 5
609
610int frustumDrawingMode = FRUSTUM_DRAW_MODE_ALL; // the mode we're drawing the frustum in, see notes above
611
612void renderViewFrustum(ViewFrustum& viewFrustum) {
613
614 // Load it with the latest details!
615 loadViewFrustum(viewFrustum);
616
617 glm::vec3 position = viewFrustum.getPosition();
618 glm::vec3 direction = viewFrustum.getDirection();
619 glm::vec3 up = viewFrustum.getUp();
620 glm::vec3 right = viewFrustum.getRight();
621
622 // Get ready to draw some lines
623 glDisable(GL_LIGHTING);
624 glColor4f(1.0, 1.0, 1.0, 1.0);
625 glLineWidth(1.0);
626 glBegin(GL_LINES);
627
628 if (::frustumDrawingMode == FRUSTUM_DRAW_MODE_ALL || ::frustumDrawingMode == FRUSTUM_DRAW_MODE_VECTORS) {
629 // Calculate the origin direction vectors
630 glm::vec3 lookingAt = position + (direction * 0.2f);
631 glm::vec3 lookingAtUp = position + (up * 0.2f);
632 glm::vec3 lookingAtRight = position + (right * 0.2f);
633
634 // Looking At = white
635 glColor3f(1,1,1);
636 glVertex3f(position.x, position.y, position.z);
637 glVertex3f(lookingAt.x, lookingAt.y, lookingAt.z);
638
639 // Looking At Up = purple
640 glColor3f(1,0,1);
641 glVertex3f(position.x, position.y, position.z);
642 glVertex3f(lookingAtUp.x, lookingAtUp.y, lookingAtUp.z);
643
644 // Looking At Right = cyan
645 glColor3f(0,1,1);
646 glVertex3f(position.x, position.y, position.z);
647 glVertex3f(lookingAtRight.x, lookingAtRight.y, lookingAtRight.z);
648 }
649
650 if (::frustumDrawingMode == FRUSTUM_DRAW_MODE_ALL || ::frustumDrawingMode == FRUSTUM_DRAW_MODE_PLANES
651 || ::frustumDrawingMode == FRUSTUM_DRAW_MODE_NEAR_PLANE) {
652 // Drawing the bounds of the frustum
653 // viewFrustum.getNear plane - bottom edge
654 glColor3f(1,0,0);
655 glVertex3f(viewFrustum.getNearBottomLeft().x, viewFrustum.getNearBottomLeft().y, viewFrustum.getNearBottomLeft().z);
656 glVertex3f(viewFrustum.getNearBottomRight().x, viewFrustum.getNearBottomRight().y, viewFrustum.getNearBottomRight().z);
657
658 // viewFrustum.getNear plane - top edge
659 glVertex3f(viewFrustum.getNearTopLeft().x, viewFrustum.getNearTopLeft().y, viewFrustum.getNearTopLeft().z);
660 glVertex3f(viewFrustum.getNearTopRight().x, viewFrustum.getNearTopRight().y, viewFrustum.getNearTopRight().z);
661
662 // viewFrustum.getNear plane - right edge
663 glVertex3f(viewFrustum.getNearBottomRight().x, viewFrustum.getNearBottomRight().y, viewFrustum.getNearBottomRight().z);
664 glVertex3f(viewFrustum.getNearTopRight().x, viewFrustum.getNearTopRight().y, viewFrustum.getNearTopRight().z);
665
666 // viewFrustum.getNear plane - left edge
667 glVertex3f(viewFrustum.getNearBottomLeft().x, viewFrustum.getNearBottomLeft().y, viewFrustum.getNearBottomLeft().z);
668 glVertex3f(viewFrustum.getNearTopLeft().x, viewFrustum.getNearTopLeft().y, viewFrustum.getNearTopLeft().z);
669 }
670
671 if (::frustumDrawingMode == FRUSTUM_DRAW_MODE_ALL || ::frustumDrawingMode == FRUSTUM_DRAW_MODE_PLANES
672 || ::frustumDrawingMode == FRUSTUM_DRAW_MODE_FAR_PLANE) {
673 // viewFrustum.getFar plane - bottom edge
674 glColor3f(0,1,0); // GREEN!!!
675 glVertex3f(viewFrustum.getFarBottomLeft().x, viewFrustum.getFarBottomLeft().y, viewFrustum.getFarBottomLeft().z);
676 glVertex3f(viewFrustum.getFarBottomRight().x, viewFrustum.getFarBottomRight().y, viewFrustum.getFarBottomRight().z);
677
678 // viewFrustum.getFar plane - top edge
679 glVertex3f(viewFrustum.getFarTopLeft().x, viewFrustum.getFarTopLeft().y, viewFrustum.getFarTopLeft().z);
680 glVertex3f(viewFrustum.getFarTopRight().x, viewFrustum.getFarTopRight().y, viewFrustum.getFarTopRight().z);
681
682 // viewFrustum.getFar plane - right edge
683 glVertex3f(viewFrustum.getFarBottomRight().x, viewFrustum.getFarBottomRight().y, viewFrustum.getFarBottomRight().z);
684 glVertex3f(viewFrustum.getFarTopRight().x, viewFrustum.getFarTopRight().y, viewFrustum.getFarTopRight().z);
685
686 // viewFrustum.getFar plane - left edge
687 glVertex3f(viewFrustum.getFarBottomLeft().x, viewFrustum.getFarBottomLeft().y, viewFrustum.getFarBottomLeft().z);
688 glVertex3f(viewFrustum.getFarTopLeft().x, viewFrustum.getFarTopLeft().y, viewFrustum.getFarTopLeft().z);
689 }
690
691 if (::frustumDrawingMode == FRUSTUM_DRAW_MODE_ALL || ::frustumDrawingMode == FRUSTUM_DRAW_MODE_PLANES) {
692 // RIGHT PLANE IS CYAN
693 // right plane - bottom edge - viewFrustum.getNear to distant
694 glColor3f(0,1,1);
695 glVertex3f(viewFrustum.getNearBottomRight().x, viewFrustum.getNearBottomRight().y, viewFrustum.getNearBottomRight().z);
696 glVertex3f(viewFrustum.getFarBottomRight().x, viewFrustum.getFarBottomRight().y, viewFrustum.getFarBottomRight().z);
697
698 // right plane - top edge - viewFrustum.getNear to distant
699 glVertex3f(viewFrustum.getNearTopRight().x, viewFrustum.getNearTopRight().y, viewFrustum.getNearTopRight().z);
700 glVertex3f(viewFrustum.getFarTopRight().x, viewFrustum.getFarTopRight().y, viewFrustum.getFarTopRight().z);
701
702 // LEFT PLANE IS BLUE
703 // left plane - bottom edge - viewFrustum.getNear to distant
704 glColor3f(0,0,1);
705 glVertex3f(viewFrustum.getNearBottomLeft().x, viewFrustum.getNearBottomLeft().y, viewFrustum.getNearBottomLeft().z);
706 glVertex3f(viewFrustum.getFarBottomLeft().x, viewFrustum.getFarBottomLeft().y, viewFrustum.getFarBottomLeft().z);
707
708 // left plane - top edge - viewFrustum.getNear to distant
709 glVertex3f(viewFrustum.getNearTopLeft().x, viewFrustum.getNearTopLeft().y, viewFrustum.getNearTopLeft().z);
710 glVertex3f(viewFrustum.getFarTopLeft().x, viewFrustum.getFarTopLeft().y, viewFrustum.getFarTopLeft().z);
711 }
712
713 glEnd();
714 glEnable(GL_LIGHTING);
715}
716
717// displays a single side (left, right, or combined for non-Oculus)
718void displaySide(Camera& whichCamera) {
719 glPushMatrix();
720
721 if (::renderStarsOn) {
722 // should be the first rendering pass - w/o depth buffer / lighting
723
724 // compute starfield alpha based on distance from atmosphere
725 float alpha = 1.0f;
726 if (::renderAtmosphereOn) {
727 float height = glm::distance(whichCamera.getPosition(), environment.getAtmosphereCenter());
728 if (height < environment.getAtmosphereInnerRadius()) {
729 alpha = 0.0f;
730
731 } else if (height < environment.getAtmosphereOuterRadius()) {
732 alpha = (height - environment.getAtmosphereInnerRadius()) /
733 (environment.getAtmosphereOuterRadius() - environment.getAtmosphereInnerRadius());
734 }
735 }
736
737 // finally render the starfield
738 stars.render(whichCamera.getFieldOfView(), whichCamera.getAspectRatio(), whichCamera.getNearClip(), alpha);
739 }
740
741 // draw the sky dome
742 if (::renderAtmosphereOn) {
743 environment.renderAtmosphere(whichCamera);
744 }
745
746 glEnable(GL_LIGHTING);
747 glEnable(GL_DEPTH_TEST);
748
749 // draw a red sphere
750 float sphereRadius = 0.25f;
751 glColor3f(1,0,0);
752 glPushMatrix();
753 glutSolidSphere(sphereRadius, 15, 15);
754 glPopMatrix();
755
756 //draw a grid ground plane....
757 drawGroundPlaneGrid(10.f);
758
759 // Draw voxels
760 if (renderVoxels) {
761 voxels.render();
762 }
763
764 // indicate what we'll be adding/removing in mouse mode, if anything
765 if (::mouseVoxel.s != 0) {
766 glPushMatrix();
767 if (::mouseMode == ADD_VOXEL_MODE) {
768 // use a contrasting color so that we can see what we're doing
769 glColor3ub(::mouseVoxel.red + 128, ::mouseVoxel.green + 128, ::mouseVoxel.blue + 128);
770 } else {
771 glColor3ub(::mouseVoxel.red, ::mouseVoxel.green, ::mouseVoxel.blue);
772 }
773 glScalef(TREE_SCALE, TREE_SCALE, TREE_SCALE);
774 glTranslatef(::mouseVoxel.x + ::mouseVoxel.s*0.5f,
775 ::mouseVoxel.y + ::mouseVoxel.s*0.5f,
776 ::mouseVoxel.z + ::mouseVoxel.s*0.5f);
777 glLineWidth(4.0f);
778 glutWireCube(::mouseVoxel.s);
779 glLineWidth(1.0f);
780 glPopMatrix();
781 }
782
783 if (::renderAvatarsOn) {
784 // Render avatars of other agents
785 AgentList* agentList = AgentList::getInstance();
786 agentList->lock();
787 for (AgentList::iterator agent = agentList->begin(); agent != agentList->end(); agent++) {
788 if (agent->getLinkedData() != NULL && agent->getType() == AGENT_TYPE_AVATAR) {
789 Avatar *avatar = (Avatar *)agent->getLinkedData();
790 avatar->render(0, ::myCamera.getPosition());
791 }
792 }
793 agentList->unlock();
794
795 // Render my own Avatar
796 myAvatar.render(::lookingInMirror, ::myCamera.getPosition());
797 }
798
799 // Render the world box
800 if (!::lookingInMirror && ::renderStatsOn) { render_world_box(); }
801
802 // brad's frustum for debugging
803 if (::frustumOn) renderViewFrustum(::viewFrustum);
804
805 glPopMatrix();
806}
807
808// this shader is an adaptation (HLSL -> GLSL, removed conditional) of the one in the Oculus sample
809// code (Samples/OculusRoomTiny/RenderTiny_D3D1X_Device.cpp), which is under the Apache license
810// (http://www.apache.org/licenses/LICENSE-2.0)
811const char* DISTORTION_FRAGMENT_SHADER =
812 "#version 120\n"
813 "uniform sampler2D texture;"
814 "uniform vec2 lensCenter;"
815 "uniform vec2 screenCenter;"
816 "uniform vec2 scale;"
817 "uniform vec2 scaleIn;"
818 "uniform vec4 hmdWarpParam;"
819 "vec2 hmdWarp(vec2 in01) {"
820 " vec2 theta = (in01 - lensCenter) * scaleIn;"
821 " float rSq = theta.x * theta.x + theta.y * theta.y;"
822 " vec2 theta1 = theta * (hmdWarpParam.x + hmdWarpParam.y * rSq + "
823 " hmdWarpParam.z * rSq * rSq + hmdWarpParam.w * rSq * rSq * rSq);"
824 " return lensCenter + scale * theta1;"
825 "}"
826 "void main(void) {"
827 " vec2 tc = hmdWarp(gl_TexCoord[0].st);"
828 " vec2 below = step(screenCenter.st + vec2(-0.25, -0.5), tc.st);"
829 " vec2 above = vec2(1.0, 1.0) - step(screenCenter.st + vec2(0.25, 0.5), tc.st);"
830 " gl_FragColor = mix(vec4(0.0, 0.0, 0.0, 1.0), texture2D(texture, tc), "
831 " above.s * above.t * below.s * below.t);"
832 "}";
833
834// the locations of the uniform variables
835int textureLocation;
836int lensCenterLocation;
837int screenCenterLocation;
838int scaleLocation;
839int scaleInLocation;
840int hmdWarpParamLocation;
841
842// renders both sides into a texture, then renders the texture to the display with distortion
843void displayOculus(Camera& whichCamera) {
844 // magic numbers ahoy! in order to avoid pulling in the Oculus utility library that calculates
845 // the rendering parameters from the hardware stats, i just folded their calculations into
846 // constants using the stats for the current-model hardware as contained in the SDK file
847 // LibOVR/Src/Util/Util_Render_Stereo.cpp
848
849 // eye
850
851 // render the left eye view to the left side of the screen
852 glMatrixMode(GL_PROJECTION);
853 glPushMatrix();
854 glLoadIdentity();
855 glTranslatef(0.151976, 0, 0); // +h, see Oculus SDK docs p. 26
856 gluPerspective(whichCamera.getFieldOfView(), whichCamera.getAspectRatio(),
857 whichCamera.getNearClip(), whichCamera.getFarClip());
858 glTranslatef(0.032, 0, 0); // dip/2, see p. 27
859
860 glMatrixMode(GL_MODELVIEW);
861 glViewport(0, 0, ::screenWidth / 2, ::screenHeight);
862 displaySide(whichCamera);
863
864 // and the right eye to the right side
865 glMatrixMode(GL_PROJECTION);
866 glLoadIdentity();
867 glTranslatef(-0.151976, 0, 0); // -h
868 gluPerspective(whichCamera.getFieldOfView(), whichCamera.getAspectRatio(),
869 whichCamera.getNearClip(), whichCamera.getFarClip());
870 glTranslatef(-0.032, 0, 0);
871
872 glMatrixMode(GL_MODELVIEW);
873 glViewport(::screenWidth / 2, 0, ::screenWidth / 2, ::screenHeight);
874 displaySide(whichCamera);
875
876 glPopMatrix();
877
878 // restore our normal viewport
879 glViewport(0, 0, ::screenWidth, ::screenHeight);
880
881 if (::oculusTextureID == 0) {
882 glGenTextures(1, &::oculusTextureID);
883 glBindTexture(GL_TEXTURE_2D, ::oculusTextureID);
884 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, ::screenWidth, ::screenHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
885 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
886
887 ::oculusProgram = new ProgramObject();
888 ::oculusProgram->attachFromSourceCode(GL_FRAGMENT_SHADER_ARB, DISTORTION_FRAGMENT_SHADER);
889 ::oculusProgram->link();
890
891 textureLocation = ::oculusProgram->getUniformLocation("texture");
892 lensCenterLocation = ::oculusProgram->getUniformLocation("lensCenter");
893 screenCenterLocation = ::oculusProgram->getUniformLocation("screenCenter");
894 scaleLocation = ::oculusProgram->getUniformLocation("scale");
895 scaleInLocation = ::oculusProgram->getUniformLocation("scaleIn");
896 hmdWarpParamLocation = ::oculusProgram->getUniformLocation("hmdWarpParam");
897
898 } else {
899 glBindTexture(GL_TEXTURE_2D, ::oculusTextureID);
900 }
901 glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, ::screenWidth, ::screenHeight);
902
903 glMatrixMode(GL_PROJECTION);
904 glLoadIdentity();
905 gluOrtho2D(0, ::screenWidth, 0, ::screenHeight);
906 glDisable(GL_DEPTH_TEST);
907 glDisable(GL_LIGHTING);
908
909 // for reference on setting these values, see SDK file Samples/OculusRoomTiny/RenderTiny_Device.cpp
910
911 float scaleFactor = 1.0 / ::oculusDistortionScale;
912 float aspectRatio = (::screenWidth * 0.5) / ::screenHeight;
913
914 glDisable(GL_BLEND);
915 glEnable(GL_TEXTURE_2D);
916 ::oculusProgram->bind();
917 ::oculusProgram->setUniform(textureLocation, 0);
918 ::oculusProgram->setUniform(lensCenterLocation, 0.287994, 0.5); // see SDK docs, p. 29
919 ::oculusProgram->setUniform(screenCenterLocation, 0.25, 0.5);
920 ::oculusProgram->setUniform(scaleLocation, 0.25 * scaleFactor, 0.5 * scaleFactor * aspectRatio);
921 ::oculusProgram->setUniform(scaleInLocation, 4, 2 / aspectRatio);
922 ::oculusProgram->setUniform(hmdWarpParamLocation, 1.0, 0.22, 0.24, 0);
923
924 glColor3f(1, 0, 1);
925 glBegin(GL_QUADS);
926 glTexCoord2f(0, 0);
927 glVertex2f(0, 0);
928 glTexCoord2f(0.5, 0);
929 glVertex2f(::screenWidth/2, 0);
930 glTexCoord2f(0.5, 1);
931 glVertex2f(::screenWidth / 2, ::screenHeight);
932 glTexCoord2f(0, 1);
933 glVertex2f(0, ::screenHeight);
934 glEnd();
935
936 ::oculusProgram->setUniform(lensCenterLocation, 0.787994, 0.5);
937 ::oculusProgram->setUniform(screenCenterLocation, 0.75, 0.5);
938
939 glBegin(GL_QUADS);
940 glTexCoord2f(0.5, 0);
941 glVertex2f(::screenWidth / 2, 0);
942 glTexCoord2f(1, 0);
943 glVertex2f(::screenWidth, 0);
944 glTexCoord2f(1, 1);
945 glVertex2f(::screenWidth, ::screenHeight);
946 glTexCoord2f(0.5, 1);
947 glVertex2f(::screenWidth / 2, ::screenHeight);
948 glEnd();
949
950 glEnable(GL_BLEND);
951 glDisable(GL_TEXTURE_2D);
952 glBindTexture(GL_TEXTURE_2D, 0);
953 ::oculusProgram->release();
954
955 glPopMatrix();
956}
957
958void displayOverlay() {
959 // Render 2D overlay: I/O level bar graphs and text
960 glMatrixMode(GL_PROJECTION);
961 glPushMatrix();
962 glLoadIdentity();
963 gluOrtho2D(0, ::screenWidth, ::screenHeight, 0);
964 glDisable(GL_DEPTH_TEST);
965 glDisable(GL_LIGHTING);
966
967 #ifndef _WIN32
968 audio.render(::screenWidth, ::screenHeight);
969 audioScope.render(20, ::screenHeight - 200);
970 #endif
971
972 //noiseTest(::screenWidth, ::screenHeight);
973
974 if (displayHeadMouse && !::lookingInMirror && USING_INVENSENSE_MPU9150) {
975 // Display small target box at center or head mouse target that can also be used to measure LOD
976 glColor3f(1.0, 1.0, 1.0);
977 glDisable(GL_LINE_SMOOTH);
978 const int PIXEL_BOX = 20;
979 glBegin(GL_LINE_STRIP);
980 glVertex2f(headMouseX - PIXEL_BOX/2, headMouseY - PIXEL_BOX/2);
981 glVertex2f(headMouseX + PIXEL_BOX/2, headMouseY - PIXEL_BOX/2);
982 glVertex2f(headMouseX + PIXEL_BOX/2, headMouseY + PIXEL_BOX/2);
983 glVertex2f(headMouseX - PIXEL_BOX/2, headMouseY + PIXEL_BOX/2);
984 glVertex2f(headMouseX - PIXEL_BOX/2, headMouseY - PIXEL_BOX/2);
985 glEnd();
986 glEnable(GL_LINE_SMOOTH);
987 }
988
989 // Show detected levels from the serial I/O ADC channel sensors
990 if (displayLevels) serialPort.renderLevels(::screenWidth,::screenHeight);
991
992 // Display stats and log text onscreen
993 glLineWidth(1.0f);
994 glPointSize(1.0f);
995
996 if (::renderStatsOn) { displayStats(); }
997 if (::logOn) { logger.render(::screenWidth, ::screenHeight); }
998
999 // Show menu
1000 if (::menuOn) {
1001 glLineWidth(1.0f);
1002 glPointSize(1.0f);
1003 menu.render(::screenWidth,::screenHeight);
1004 }
1005
1006 // Show chat entry field
1007 if (::chatEntryOn) {
1008 chatEntry.render(::screenWidth, ::screenHeight);
1009 }
1010
1011 // Stats at upper right of screen about who domain server is telling us about
1012 glPointSize(1.0f);
1013 char agents[100];
1014
1015 AgentList* agentList = AgentList::getInstance();
1016 int totalAvatars = 0, totalServers = 0;
1017
1018 for (AgentList::iterator agent = agentList->begin(); agent != agentList->end(); agent++) {
1019 agent->getType() == AGENT_TYPE_AVATAR ? totalAvatars++ : totalServers++;
1020 }
1021
1022 sprintf(agents, "Servers: %d, Avatars: %d\n", totalServers, totalAvatars);
1023 drawtext(::screenWidth - 150, 20, 0.10, 0, 1.0, 0, agents, 1, 0, 0);
1024
1025 if (::paintOn) {
1026
1027 char paintMessage[100];
1028 sprintf(paintMessage,"Painting (%.3f,%.3f,%.3f/%.3f/%d,%d,%d)",
1029 ::paintingVoxel.x,::paintingVoxel.y,::paintingVoxel.z,::paintingVoxel.s,
1030 (unsigned int)::paintingVoxel.red,(unsigned int)::paintingVoxel.green,(unsigned int)::paintingVoxel.blue);
1031 drawtext(::screenWidth - 350, 50, 0.10, 0, 1.0, 0, paintMessage, 1, 1, 0);
1032 }
1033
1034 glPopMatrix();
1035}
1036
1037void display(void)
1038{
1039 PerfStat("display");
1040
1041 glEnable(GL_LINE_SMOOTH);
1042 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1043 glMatrixMode(GL_MODELVIEW);
1044
1045 glPushMatrix(); {
1046 glLoadIdentity();
1047
1048 // camera settings
1049 if (myCamera.getMode() == CAMERA_MODE_MIRROR) {
1050 myAvatar.setDisplayingHead(true);
1051 myCamera.setUpShift (0.0);
1052 myCamera.setDistance (0.2);
1053 myCamera.setTightness (100.0f);
1054 myCamera.setTargetPosition(myAvatar.getHeadPosition());
1055 myCamera.setTargetRotation(myAvatar.getBodyYaw() - 180.0f, 0.0f, 0.0f);
1056
1057 } else if (myCamera.getMode() == CAMERA_MODE_FIRST_PERSON || OculusManager::isConnected()) {
1058 myAvatar.setDisplayingHead(false);
1059 myCamera.setUpShift (0.0f);
1060 myCamera.setDistance (0.0f);
1061 myCamera.setTightness (100.0f);
1062 myCamera.setTargetPosition(myAvatar.getHeadPosition());
1063
1064 if (OculusManager::isConnected()) {
1065 myCamera.setTargetRotation(myAvatar.getBodyYaw() + myAvatar.getHeadYaw(),
1066 -myAvatar.getHeadPitch(),
1067 myAvatar.getHeadRoll());
1068 } else {
1069 myCamera.setTargetRotation(myAvatar.getAbsoluteHeadYaw()- mouseViewShiftYaw, myAvatar.getAbsoluteHeadPitch() + myAvatar.getRenderPitch() + mouseViewShiftPitch, 0.0f);
1070 }
1071 } else if (myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) {
1072 myAvatar.setDisplayingHead(true);
1073 myCamera.setUpShift (-0.2f);
1074 myCamera.setDistance (1.5f);
1075 myCamera.setTightness (8.0f);
1076 myCamera.setTargetPosition(myAvatar.getHeadPosition());
1077 myCamera.setTargetRotation(myAvatar.getBodyYaw() - mouseViewShiftYaw, mouseViewShiftPitch, 0.0f);
1078 }
1079
1080 // important...
1081 myCamera.update( 1.f/FPS );
1082
1083 // Render anything (like HUD items) that we want to be in 3D but not in worldspace
1084 /*
1085 const float HUD_Z_OFFSET = -5.f;
1086 glPushMatrix();
1087 glm::vec3 test(0.5, 0.5, 0.5);
1088 glTranslatef(1, 1, HUD_Z_OFFSET);
1089 drawVector(&test);
1090 glPopMatrix();
1091 */
1092
1093
1094 // Note: whichCamera is used to pick between the normal camera myCamera for our
1095 // main camera, vs, an alternate camera. The alternate camera we support right now
1096 // is the viewFrustumOffsetCamera. But theoretically, we could use this same mechanism
1097 // to add other cameras.
1098 //
1099 // Why have two cameras? Well, one reason is that because in the case of the renderViewFrustum()
1100 // code, we want to keep the state of "myCamera" intact, so we can render what the view frustum of
1101 // myCamera is. But we also want to do meaningful camera transforms on OpenGL for the offset camera
1102 Camera whichCamera = myCamera;
1103 Camera viewFrustumOffsetCamera = myCamera;
1104
1105 if (::viewFrustumFromOffset && ::frustumOn) {
1106
1107 // set the camera to third-person view but offset so we can see the frustum
1108 viewFrustumOffsetCamera.setTargetYaw(::viewFrustumOffsetYaw + myAvatar.getBodyYaw());
1109 viewFrustumOffsetCamera.setPitch (::viewFrustumOffsetPitch );
1110 viewFrustumOffsetCamera.setRoll (::viewFrustumOffsetRoll );
1111 viewFrustumOffsetCamera.setUpShift (::viewFrustumOffsetUp );
1112 viewFrustumOffsetCamera.setDistance (::viewFrustumOffsetDistance);
1113 viewFrustumOffsetCamera.update(1.f/FPS);
1114 whichCamera = viewFrustumOffsetCamera;
1115 }
1116
1117 // transform view according to whichCamera
1118 // could be myCamera (if in normal mode)
1119 // or could be viewFrustumOffsetCamera if in offset mode
1120 // I changed the ordering here - roll is FIRST (JJV)
1121
1122 glRotatef ( whichCamera.getRoll(), IDENTITY_FRONT.x, IDENTITY_FRONT.y, IDENTITY_FRONT.z);
1123 glRotatef ( whichCamera.getPitch(), IDENTITY_RIGHT.x, IDENTITY_RIGHT.y, IDENTITY_RIGHT.z);
1124 glRotatef (180.0 - whichCamera.getYaw(), IDENTITY_UP.x, IDENTITY_UP.y, IDENTITY_UP.z );
1125
1126 glTranslatef(-whichCamera.getPosition().x, -whichCamera.getPosition().y, -whichCamera.getPosition().z);
1127
1128 // Setup 3D lights (after the camera transform, so that they are positioned in world space)
1129 glEnable(GL_COLOR_MATERIAL);
1130 glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);
1131
1132 GLfloat light_position0[] = { 1.0, 1.0, 0.0, 0.0 };
1133 glLightfv(GL_LIGHT0, GL_POSITION, light_position0);
1134 GLfloat ambient_color[] = { 0.7, 0.7, 0.8 };
1135 glLightfv(GL_LIGHT0, GL_AMBIENT, ambient_color);
1136 GLfloat diffuse_color[] = { 0.8, 0.7, 0.7 };
1137 glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse_color);
1138 GLfloat specular_color[] = { 1.0, 1.0, 1.0, 1.0};
1139 glLightfv(GL_LIGHT0, GL_SPECULAR, specular_color);
1140
1141 glMaterialfv(GL_FRONT, GL_SPECULAR, specular_color);
1142 glMateriali(GL_FRONT, GL_SHININESS, 96);
1143
1144 if (::oculusOn) {
1145 displayOculus(whichCamera);
1146
1147 } else {
1148 displaySide(whichCamera);
1149 glPopMatrix();
1150
1151 displayOverlay();
1152 }
1153 }
1154
1155 glutSwapBuffers();
1156 frameCount++;
1157
1158 // If application has just started, report time from startup to now (first frame display)
1159 if (justStarted) {
1160 float startupTime = (usecTimestampNow() - usecTimestamp(&applicationStartupTime))/1000000.0;
1161 justStarted = false;
1162 char title[30];
1163 snprintf(title, 30, "Interface: %4.2f seconds", startupTime);
1164 glutSetWindowTitle(title);
1165 }
1166}
1167
1168// int version of setValue()
1169int setValue(int state, int *value) {
1170 if (state == MENU_ROW_PICKED) {
1171 *value = !(*value);
1172 } else if (state == MENU_ROW_GET_VALUE) {
1173 return *value;
1174 } else {
1175 *value = state;
1176 }
1177 return *value;
1178}
1179
1180// bool version of setValue()
1181int setValue(int state, bool *value) {
1182 if (state == MENU_ROW_PICKED) {
1183 *value = !(*value);
1184 } else if (state == MENU_ROW_GET_VALUE) {
1185 return *value;
1186 } else {
1187 *value = state;
1188 }
1189 return *value;
1190}
1191
1192int setHead(int state) {
1193 return setValue(state, &::lookingInMirror);
1194}
1195
1196int setNoise(int state) {
1197 int iRet = setValue(state, &noiseOn);
1198 if (noiseOn) {
1199 myAvatar.setNoise(noise);
1200 } else {
1201 myAvatar.setNoise(0);
1202 }
1203 return iRet;
1204}
1205
1206int setLog(int state) {
1207 int iRet = setValue(state, &::logOn);
1208 return iRet;
1209}
1210
1211int setGyroLook(int state) {
1212 int iRet = setValue(state, &::gyroLook);
1213 return iRet;
1214}
1215
1216int setFullscreen(int state) {
1217 bool wasFullscreen = ::fullscreen;
1218 int value = setValue(state, &::fullscreen);
1219 if (::fullscreen != wasFullscreen) {
1220 if (::fullscreen) {
1221 glutFullScreen();
1222
1223 } else {
1224 glutReshapeWindow(::screenWidth, ::screenHeight);
1225 }
1226 }
1227 return value;
1228}
1229
1230int setVoxels(int state) {
1231 return setValue(state, &::renderVoxels);
1232}
1233
1234int setStars(int state) {
1235 return setValue(state, &::renderStarsOn);
1236}
1237
1238int setAtmosphere(int state) {
1239 return setValue(state, &::renderAtmosphereOn);
1240}
1241
1242int setRenderAvatars(int state) {
1243 return setValue(state, &::renderAvatarsOn);
1244}
1245
1246int setRenderFirstPerson(int state) {
1247 bool value = setValue(state, &::renderFirstPersonOn);
1248 if (state == MENU_ROW_PICKED) {
1249 if (::renderFirstPersonOn) {
1250 myCamera.setMode(CAMERA_MODE_FIRST_PERSON);
1251 } else {
1252 myCamera.setMode(CAMERA_MODE_THIRD_PERSON);
1253 }
1254 }
1255 return value;
1256}
1257
1258int setOculus(int state) {
1259 bool wasOn = ::oculusOn;
1260 int value = setValue(state, &::oculusOn);
1261 if (::oculusOn != wasOn) {
1262 reshape(::screenWidth, ::screenHeight);
1263 }
1264 return value;
1265}
1266
1267int setStats(int state) {
1268 return setValue(state, &::renderStatsOn);
1269}
1270
1271int setMenu(int state) {
1272 return setValue(state, &::menuOn);
1273}
1274
1275int setRenderWarnings(int state) {
1276 int value = setValue(state, &::renderWarningsOn);
1277 if (state == MENU_ROW_PICKED) {
1278 ::voxels.setRenderPipelineWarnings(::renderWarningsOn);
1279 }
1280 return value;
1281}
1282
1283int setWantResIn(int state) {
1284 int value = setValue(state, &::wantResIn);
1285 if (state == MENU_ROW_PICKED) {
1286 ::myAvatar.setWantResIn(::wantResIn);
1287 }
1288 return value;
1289}
1290
1291int setWantMonochrome(int state) {
1292 int value = setValue(state, &::wantMonochrome);
1293 if (state == MENU_ROW_PICKED) {
1294 ::myAvatar.setWantColor(!::wantMonochrome);
1295 }
1296 return value;
1297}
1298
1299int setDisplayFrustum(int state) {
1300 return setValue(state, &::frustumOn);
1301}
1302
1303int setFrustumOffset(int state) {
1304 int value = setValue(state, &::viewFrustumFromOffset);
1305
1306 // reshape so that OpenGL will get the right lens details for the camera of choice
1307 if (state == MENU_ROW_PICKED) {
1308 reshape(::screenWidth, ::screenHeight);
1309 }
1310
1311 return value;
1312}
1313
1314int setFrustumOrigin(int state) {
1315 return setValue(state, &::cameraFrustum);
1316}
1317
1318int quitApp(int state) {
1319 if (state == MENU_ROW_PICKED) {
1320 ::terminate();
1321 }
1322 return 2; // non state so menu class doesn't add "state"
1323}
1324
1325int setFrustumRenderMode(int state) {
1326 if (state == MENU_ROW_PICKED) {
1327 ::frustumDrawingMode = (::frustumDrawingMode+1)%FRUSTUM_DRAW_MODE_COUNT;
1328 }
1329 return ::frustumDrawingMode;
1330}
1331
1332int doKillLocalVoxels(int state) {
1333 if (state == MENU_ROW_PICKED) {
1334 ::wantToKillLocalVoxels = true;
1335 }
1336 return state;
1337}
1338
1339int doRandomizeVoxelColors(int state) {
1340 if (state == MENU_ROW_PICKED) {
1341 ::voxels.randomizeVoxelColors();
1342 }
1343 return state;
1344}
1345
1346int doFalseRandomizeEveryOtherVoxelColors(int state) {
1347 if (state == MENU_ROW_PICKED) {
1348 ::voxels.falseColorizeRandomEveryOther();
1349 }
1350 return state;
1351}
1352
1353int doTreeStats(int state) {
1354 if (state == MENU_ROW_PICKED) {
1355 ::voxels.collectStatsForTreesAndVBOs();
1356 }
1357 return state;
1358}
1359
1360int doFalseRandomizeVoxelColors(int state) {
1361 if (state == MENU_ROW_PICKED) {
1362 ::voxels.falseColorizeRandom();
1363 }
1364 return state;
1365}
1366
1367int doTrueVoxelColors(int state) {
1368 if (state == MENU_ROW_PICKED) {
1369 ::voxels.trueColorize();
1370 }
1371 return state;
1372}
1373
1374int doFalseColorizeByDistance(int state) {
1375 if (state == MENU_ROW_PICKED) {
1376 loadViewFrustum(::viewFrustum);
1377 voxels.falseColorizeDistanceFromView(&::viewFrustum);
1378 }
1379 return state;
1380}
1381
1382int doFalseColorizeInView(int state) {
1383 if (state == MENU_ROW_PICKED) {
1384 loadViewFrustum(::viewFrustum);
1385 // we probably want to make sure the viewFrustum is initialized first
1386 voxels.falseColorizeInView(&::viewFrustum);
1387 }
1388 return state;
1389}
1390
1391
1392
1393const char* modeAll = " - All ";
1394const char* modeVectors = " - Vectors ";
1395const char* modePlanes = " - Planes ";
1396const char* modeNear = " - Near ";
1397const char* modeFar = " - Far ";
1398
1399const char* getFrustumRenderModeName(int state) {
1400 const char * mode;
1401 switch (state) {
1402 case FRUSTUM_DRAW_MODE_ALL:
1403 mode = modeAll;
1404 break;
1405 case FRUSTUM_DRAW_MODE_VECTORS:
1406 mode = modeVectors;
1407 break;
1408 case FRUSTUM_DRAW_MODE_PLANES:
1409 mode = modePlanes;
1410 break;
1411 case FRUSTUM_DRAW_MODE_NEAR_PLANE:
1412 mode = modeNear;
1413 break;
1414 case FRUSTUM_DRAW_MODE_FAR_PLANE:
1415 mode = modeFar;
1416 break;
1417 }
1418 return mode;
1419}
1420
1421void initMenu() {
1422 MenuColumn *menuColumnOptions, *menuColumnRender, *menuColumnTools, *menuColumnDebug, *menuColumnFrustum;
1423 // Options
1424 menuColumnOptions = menu.addColumn("Options");
1425 menuColumnOptions->addRow("Mirror (h)", setHead);
1426 menuColumnOptions->addRow("Noise (n)", setNoise);
1427 menuColumnOptions->addRow("Gyro Look", setGyroLook);
1428 menuColumnOptions->addRow("Fullscreen (f)", setFullscreen);
1429 menuColumnOptions->addRow("Quit (q)", quitApp);
1430
1431 // Render
1432 menuColumnRender = menu.addColumn("Render");
1433 menuColumnRender->addRow("Voxels (V)", setVoxels);
1434 menuColumnRender->addRow("Stars (*)", setStars);
1435 menuColumnRender->addRow("Atmosphere (A)", setAtmosphere);
1436 menuColumnRender->addRow("Avatars", setRenderAvatars);
1437 menuColumnRender->addRow("First Person (p)", setRenderFirstPerson);
1438 menuColumnRender->addRow("Oculus (o)", setOculus);
1439
1440 // Tools
1441 menuColumnTools = menu.addColumn("Tools");
1442 menuColumnTools->addRow("Stats (/)", setStats);
1443 menuColumnTools->addRow("Log ", setLog);
1444 menuColumnTools->addRow("(M)enu", setMenu);
1445
1446 // Frustum Options
1447 menuColumnFrustum = menu.addColumn("Frustum");
1448 menuColumnFrustum->addRow("Display (F)rustum", setDisplayFrustum);
1449 menuColumnFrustum->addRow("Use (O)ffset Camera", setFrustumOffset);
1450 menuColumnFrustum->addRow("Switch (C)amera", setFrustumOrigin);
1451 menuColumnFrustum->addRow("(R)ender Mode", setFrustumRenderMode, getFrustumRenderModeName);
1452
1453 // Debug
1454 menuColumnDebug = menu.addColumn("Debug");
1455 menuColumnDebug->addRow("Show Render Pipeline Warnings", setRenderWarnings);
1456 menuColumnDebug->addRow("Kill Local Voxels", doKillLocalVoxels);
1457 menuColumnDebug->addRow("Randomize Voxel TRUE Colors", doRandomizeVoxelColors);
1458 menuColumnDebug->addRow("FALSE Color Voxels Randomly", doFalseRandomizeVoxelColors);
1459 menuColumnDebug->addRow("FALSE Color Voxel Every Other Randomly", doFalseRandomizeEveryOtherVoxelColors);
1460 menuColumnDebug->addRow("FALSE Color Voxels by Distance", doFalseColorizeByDistance);
1461 menuColumnDebug->addRow("FALSE Color Voxel Out of View", doFalseColorizeInView);
1462 menuColumnDebug->addRow("Show TRUE Colors", doTrueVoxelColors);
1463 menuColumnDebug->addRow("Calculate Tree Stats", doTreeStats);
1464 menuColumnDebug->addRow("Wants Res-In", setWantResIn);
1465 menuColumnDebug->addRow("Wants Monochrome", setWantMonochrome);
1466}
1467
1468void testPointToVoxel() {
1469 float y=0;
1470 float z=0;
1471 float s=0.1;
1472 for (float x=0; x<=1; x+= 0.05) {
1473 printLog(" x=%f");
1474
1475 unsigned char red = 200; //randomColorValue(65);
1476 unsigned char green = 200; //randomColorValue(65);
1477 unsigned char blue = 200; //randomColorValue(65);
1478
1479 unsigned char* voxelCode = pointToVoxel(x, y, z, s,red,green,blue);
1480 printVoxelCode(voxelCode);
1481 delete voxelCode;
1482 printLog("\n");
1483 }
1484}
1485
1486void sendVoxelServerEraseAll() {
1487 char message[100];
1488 sprintf(message,"%c%s",'Z',"erase all");
1489 int messageSize = strlen(message) + 1;
1490 AgentList::getInstance()->broadcastToAgents((unsigned char*) message, messageSize, &AGENT_TYPE_VOXEL, 1);
1491}
1492
1493void sendVoxelServerAddScene() {
1494 char message[100];
1495 sprintf(message,"%c%s",'Z',"add scene");
1496 int messageSize = strlen(message) + 1;
1497 AgentList::getInstance()->broadcastToAgents((unsigned char*)message, messageSize, &AGENT_TYPE_VOXEL, 1);
1498}
1499
1500void shiftPaintingColor()
1501{
1502 // About the color of the paintbrush... first determine the dominant color
1503 ::dominantColor = (::dominantColor+1)%3; // 0=red,1=green,2=blue
1504 ::paintingVoxel.red = (::dominantColor==0)?randIntInRange(200,255):randIntInRange(40,100);
1505 ::paintingVoxel.green = (::dominantColor==1)?randIntInRange(200,255):randIntInRange(40,100);
1506 ::paintingVoxel.blue = (::dominantColor==2)?randIntInRange(200,255):randIntInRange(40,100);
1507}
1508
1509void setupPaintingVoxel() {
1510 glm::vec3 avatarPos = myAvatar.getPosition();
1511
1512 ::paintingVoxel.x = avatarPos.z/-10.0; // voxel space x is negative z head space
1513 ::paintingVoxel.y = avatarPos.y/-10.0; // voxel space y is negative y head space
1514 ::paintingVoxel.z = avatarPos.x/-10.0; // voxel space z is negative x head space
1515 ::paintingVoxel.s = 1.0/256;
1516
1517 shiftPaintingColor();
1518}
1519
1520void addVoxelInFrontOfAvatar() {
1521 VoxelDetail detail;
1522
1523 glm::vec3 position = (myAvatar.getPosition() + myAvatar.getCameraDirection()) * (1.0f / TREE_SCALE);
1524 detail.s = ::mouseVoxelScale;
1525
1526 detail.x = detail.s * floor(position.x / detail.s);
1527 detail.y = detail.s * floor(position.y / detail.s);
1528 detail.z = detail.s * floor(position.z / detail.s);
1529 detail.red = 128;
1530 detail.green = 128;
1531 detail.blue = 128;
1532
1533 sendVoxelEditMessage(PACKET_HEADER_SET_VOXEL, detail);
1534
1535 // create the voxel locally so it appears immediately
1536 voxels.createVoxel(detail.x, detail.y, detail.z, detail.s, detail.red, detail.green, detail.blue);
1537}
1538
1539void addVoxelUnderCursor() {
1540 if (::mouseVoxel.s != 0) {
1541 sendVoxelEditMessage(PACKET_HEADER_SET_VOXEL, ::mouseVoxel);
1542
1543 // create the voxel locally so it appears immediately
1544 voxels.createVoxel(::mouseVoxel.x, ::mouseVoxel.y, ::mouseVoxel.z, ::mouseVoxel.s,
1545 ::mouseVoxel.red, ::mouseVoxel.green, ::mouseVoxel.blue);
1546 }
1547}
1548
1549void deleteVoxelUnderCursor() {
1550 if (::mouseVoxel.s != 0) {
1551 sendVoxelEditMessage(PACKET_HEADER_ERASE_VOXEL, ::mouseVoxel);
1552
1553 // delete the voxel locally so it disappears immediately
1554 voxels.deleteVoxelAt(::mouseVoxel.x, ::mouseVoxel.y, ::mouseVoxel.z, ::mouseVoxel.s);
1555 }
1556}
1557
1558const float KEYBOARD_YAW_RATE = 0.8;
1559const float KEYBOARD_PITCH_RATE = 0.6;
1560const float KEYBOARD_STRAFE_RATE = 0.03;
1561const float KEYBOARD_FLY_RATE = 0.08;
1562
1563void specialkeyUp(int k, int x, int y) {
1564 if (k == GLUT_KEY_UP) {
1565 myAvatar.setDriveKeys(FWD, 0);
1566 myAvatar.setDriveKeys(UP, 0);
1567 }
1568 if (k == GLUT_KEY_DOWN) {
1569 myAvatar.setDriveKeys(BACK, 0);
1570 myAvatar.setDriveKeys(DOWN, 0);
1571 }
1572 if (k == GLUT_KEY_LEFT) {
1573 myAvatar.setDriveKeys(LEFT, 0);
1574 myAvatar.setDriveKeys(ROT_LEFT, 0);
1575 }
1576 if (k == GLUT_KEY_RIGHT) {
1577 myAvatar.setDriveKeys(RIGHT, 0);
1578 myAvatar.setDriveKeys(ROT_RIGHT, 0);
1579 }
1580}
1581
1582void specialkey(int k, int x, int y) {
1583 if (::chatEntryOn) {
1584
1585 return;
1586 }
1587
1588 if (k == GLUT_KEY_UP || k == GLUT_KEY_DOWN || k == GLUT_KEY_LEFT || k == GLUT_KEY_RIGHT) {
1589 if (k == GLUT_KEY_UP) {
1590 if (glutGetModifiers() == GLUT_ACTIVE_SHIFT) myAvatar.setDriveKeys(UP, 1);
1591 else myAvatar.setDriveKeys(FWD, 1);
1592 }
1593 if (k == GLUT_KEY_DOWN) {
1594 if (glutGetModifiers() == GLUT_ACTIVE_SHIFT) myAvatar.setDriveKeys(DOWN, 1);
1595 else myAvatar.setDriveKeys(BACK, 1);
1596 }
1597 if (k == GLUT_KEY_LEFT) {
1598 if (glutGetModifiers() == GLUT_ACTIVE_SHIFT) myAvatar.setDriveKeys(LEFT, 1);
1599 else myAvatar.setDriveKeys(ROT_LEFT, 1);
1600 }
1601 if (k == GLUT_KEY_RIGHT) {
1602 if (glutGetModifiers() == GLUT_ACTIVE_SHIFT) myAvatar.setDriveKeys(RIGHT, 1);
1603 else myAvatar.setDriveKeys(ROT_RIGHT, 1);
1604 }
1605 }
1606}
1607
1608void keyUp(unsigned char k, int x, int y) {
1609 if (::chatEntryOn) {
1610 myAvatar.setKeyState(NO_KEY_DOWN);
1611 return;
1612 }
1613
1614 if (k == 'e') myAvatar.setDriveKeys(UP, 0);
1615 if (k == 'c') myAvatar.setDriveKeys(DOWN, 0);
1616 if (k == 'w') myAvatar.setDriveKeys(FWD, 0);
1617 if (k == 's') myAvatar.setDriveKeys(BACK, 0);
1618 if (k == 'a') myAvatar.setDriveKeys(ROT_LEFT, 0);
1619 if (k == 'd') myAvatar.setDriveKeys(ROT_RIGHT, 0);
1620}
1621
1622void toggleMouseMode(MouseMode mode) {
1623 ::mouseMode = (::mouseMode == mode) ? NO_EDIT_MODE : mode;
1624}
1625
1626void key(unsigned char k, int x, int y) {
1627 if (::chatEntryOn) {
1628
1629 return;
1630 }
1631
1632 // Process keypresses
1633
1634 if (k == 'S') {
1635 ::voxels.collectStatsForTreesAndVBOs();
1636 }
1637
1638 if (k == 'q' || k == 'Q') ::terminate();
1639 if (k == '/') ::renderStatsOn = !::renderStatsOn; // toggle stats
1640 if (k == '*') ::renderStarsOn = !::renderStarsOn; // toggle stars
1641 if (k == 'V' || k == 'v') ::renderVoxels = !::renderVoxels; // toggle voxels
1642 if (k == 'A') ::renderAtmosphereOn = !::renderAtmosphereOn;
1643 if (k == 'F') ::frustumOn = !::frustumOn; // toggle view frustum debugging
1644 if (k == 'C') ::cameraFrustum = !::cameraFrustum; // toggle which frustum to look at
1645 if (k == 'O' || k == 'G') setFrustumOffset(MENU_ROW_PICKED); // toggle view frustum offset debugging
1646 if (k == 'f') setFullscreen(!::fullscreen);
1647 if (k == 'o') setOculus(!::oculusOn);
1648 if (k == 'p') setRenderFirstPerson(MENU_ROW_PICKED);
1649 if (k == '[') ::viewFrustumOffsetYaw -= 0.5;
1650 if (k == ']') ::viewFrustumOffsetYaw += 0.5;
1651 if (k == '{') ::viewFrustumOffsetPitch -= 0.5;
1652 if (k == '}') ::viewFrustumOffsetPitch += 0.5;
1653 if (k == '(') ::viewFrustumOffsetRoll -= 0.5;
1654 if (k == ')') ::viewFrustumOffsetRoll += 0.5;
1655 if (k == '<') ::viewFrustumOffsetDistance -= 0.5;
1656 if (k == '>') ::viewFrustumOffsetDistance += 0.5;
1657 if (k == ',') ::viewFrustumOffsetUp -= 0.05;
1658 if (k == '.') ::viewFrustumOffsetUp += 0.05;
1659
1660// if (k == '|') ViewFrustum::fovAngleAdust -= 0.05;
1661// if (k == '\\') ViewFrustum::fovAngleAdust += 0.05;
1662
1663 if (k == 'R') setFrustumRenderMode(MENU_ROW_PICKED);
1664
1665 if (k == '&') {
1666 ::paintOn = !::paintOn; // toggle paint
1667 ::setupPaintingVoxel(); // also randomizes colors
1668 }
1669 if (k == '^') ::shiftPaintingColor(); // shifts randomize color between R,G,B dominant
1670 if (k == '-') ::sendVoxelServerEraseAll(); // sends erase all command to voxel server
1671 if (k == '%') ::sendVoxelServerAddScene(); // sends add scene command to voxel server
1672 if (k == '1') ::mouseMode = (::mouseMode == ADD_VOXEL_MODE) ? NO_EDIT_MODE : ADD_VOXEL_MODE;
1673 if (k == '2') ::mouseMode = (::mouseMode == DELETE_VOXEL_MODE) ? NO_EDIT_MODE : DELETE_VOXEL_MODE;
1674 if (k == '3') ::mouseMode = (::mouseMode == COLOR_VOXEL_MODE) ? NO_EDIT_MODE : COLOR_VOXEL_MODE;
1675 if (k == '4') addVoxelInFrontOfAvatar();
1676 if (k == '5') ::mouseVoxelScale /= 2;
1677 if (k == '6') ::mouseVoxelScale *= 2;
1678 if (k == 'n' || k == 'N')
1679 {
1680 noiseOn = !noiseOn; // Toggle noise
1681 if (noiseOn)
1682 {
1683 myAvatar.setNoise(noise);
1684 }
1685 else
1686 {
1687 myAvatar.setNoise(0);
1688 }
1689 }
1690
1691 if (k == 'h') {
1692 ::lookingInMirror = !::lookingInMirror;
1693 #ifndef _WIN32
1694 audio.setMixerLoopbackFlag(::lookingInMirror);
1695
1696 if (::lookingInMirror) {
1697 myCamera.setMode(CAMERA_MODE_MIRROR);
1698 } else {
1699 myCamera.setMode(defaultCameraMode);
1700 }
1701 #endif
1702 }
1703
1704 if (k == 'm' || k == 'M') setMenu(MENU_ROW_PICKED);
1705
1706 if (k == 'l') displayLevels = !displayLevels;
1707 if (k == 'e') myAvatar.setDriveKeys(UP, 1);
1708 if (k == 'c') myAvatar.setDriveKeys(DOWN, 1);
1709 if (k == 'w') myAvatar.setDriveKeys(FWD, 1);
1710 if (k == 's') myAvatar.setDriveKeys(BACK, 1);
1711 if (k == ' ') reset_sensors();
1712 if (k == 'a') myAvatar.setDriveKeys(ROT_LEFT, 1);
1713 if (k == 'd') myAvatar.setDriveKeys(ROT_RIGHT, 1);
1714
1715 if (k == '\r') {
1716 ::chatEntryOn = true;
1717 myAvatar.setKeyState(NO_KEY_DOWN);
1718 myAvatar.setChatMessage(string());
1719 }
1720}
1721
1722// Receive packets from other agents/servers and decide what to do with them!
1723void* networkReceive(void* args) {
1724 sockaddr senderAddress;
1725 ssize_t bytesReceived;
1726
1727 while (!stopNetworkReceiveThread) {
1728 // check to see if the UI thread asked us to kill the voxel tree. since we're the only thread allowed to do that
1729 if (::wantToKillLocalVoxels) {
1730 ::voxels.killLocalVoxels();
1731 ::wantToKillLocalVoxels = false;
1732 }
1733
1734 if (AgentList::getInstance()->getAgentSocket().receive(&senderAddress, incomingPacket, &bytesReceived)) {
1735 packetCount++;
1736 bytesCount += bytesReceived;
1737
1738 switch (incomingPacket[0]) {
1739 case PACKET_HEADER_TRANSMITTER_DATA:
1740 // Process UDP packets that are sent to the client from local sensor devices
1741 myAvatar.processTransmitterData(incomingPacket, bytesReceived);
1742 break;
1743 case PACKET_HEADER_VOXEL_DATA:
1744 case PACKET_HEADER_VOXEL_DATA_MONOCHROME:
1745 case PACKET_HEADER_Z_COMMAND:
1746 case PACKET_HEADER_ERASE_VOXEL:
1747 voxels.parseData(incomingPacket, bytesReceived);
1748 break;
1749 case PACKET_HEADER_ENVIRONMENT_DATA:
1750 environment.parseData(incomingPacket, bytesReceived);
1751 break;
1752 case PACKET_HEADER_BULK_AVATAR_DATA:
1753 AgentList::getInstance()->processBulkAgentData(&senderAddress,
1754 incomingPacket,
1755 bytesReceived);
1756 break;
1757 default:
1758 AgentList::getInstance()->processAgentData(&senderAddress, incomingPacket, bytesReceived);
1759 break;
1760 }
1761 } else if (!enableNetworkThread) {
1762 break;
1763 }
1764 }
1765
1766 if (enableNetworkThread) {
1767 pthread_exit(0);
1768 }
1769 return NULL;
1770}
1771
1772glm::vec3 getFaceVector(BoxFace face) {
1773 switch (face) {
1774 case MIN_X_FACE:
1775 return glm::vec3(-1, 0, 0);
1776
1777 case MAX_X_FACE:
1778 return glm::vec3(1, 0, 0);
1779
1780 case MIN_Y_FACE:
1781 return glm::vec3(0, -1, 0);
1782
1783 case MAX_Y_FACE:
1784 return glm::vec3(0, 1, 0);
1785
1786 case MIN_Z_FACE:
1787 return glm::vec3(0, 0, -1);
1788
1789 case MAX_Z_FACE:
1790 return glm::vec3(0, 0, 1);
1791 }
1792}
1793
1794Application* app;
1795
1796void idle(void) {
1797 timeval check;
1798 gettimeofday(&check, NULL);
1799
1800 app->processEvents();
1801
1802 // Only run simulation code if more than IDLE_SIMULATE_MSECS have passed since last time
1803
1804 if (diffclock(&lastTimeIdle, &check) > IDLE_SIMULATE_MSECS) {
1805
1806 float deltaTime = 1.f/FPS;
1807
1808 // update behaviors for avatar hand movement: handControl takes mouse values as input,
1809 // and gives back 3D values modulated for smooth transitioning between interaction modes.
1810 handControl.update(mouseX, mouseY);
1811 myAvatar.setHandMovementValues(handControl.getValues());
1812
1813 // tell my avatar if the mouse is being pressed...
1814 myAvatar.setMousePressed(mousePressed);
1815
1816 // check what's under the mouse and update the mouse voxel
1817 glm::vec3 origin, direction;
1818 viewFrustum.computePickRay(mouseX / (float)::screenWidth, mouseY / (float)::screenHeight, origin, direction);
1819
1820 float distance;
1821 BoxFace face;
1822 ::mouseVoxel.s = 0.0f;
1823 if (::mouseMode != NO_EDIT_MODE && voxels.findRayIntersection(origin, direction, ::mouseVoxel, distance, face)) {
1824 // find the nearest voxel with the desired scale
1825 if (::mouseVoxelScale > ::mouseVoxel.s) {
1826 // choose the larger voxel that encompasses the one selected
1827 ::mouseVoxel.x = ::mouseVoxelScale * floorf(::mouseVoxel.x / ::mouseVoxelScale);
1828 ::mouseVoxel.y = ::mouseVoxelScale * floorf(::mouseVoxel.y / ::mouseVoxelScale);
1829 ::mouseVoxel.z = ::mouseVoxelScale * floorf(::mouseVoxel.z / ::mouseVoxelScale);
1830 ::mouseVoxel.s = ::mouseVoxelScale;
1831
1832 } else {
1833 glm::vec3 faceVector = getFaceVector(face);
1834 if (::mouseVoxelScale < ::mouseVoxel.s) {
1835 // find the closest contained voxel
1836 glm::vec3 pt = (origin + direction * distance) / (float)TREE_SCALE -
1837 faceVector * (::mouseVoxelScale * 0.5f);
1838 ::mouseVoxel.x = ::mouseVoxelScale * floorf(pt.x / ::mouseVoxelScale);
1839 ::mouseVoxel.y = ::mouseVoxelScale * floorf(pt.y / ::mouseVoxelScale);
1840 ::mouseVoxel.z = ::mouseVoxelScale * floorf(pt.z / ::mouseVoxelScale);
1841 ::mouseVoxel.s = ::mouseVoxelScale;
1842 }
1843 if (::mouseMode == ADD_VOXEL_MODE) {
1844 // use the face to determine the side on which to create a neighbor
1845 ::mouseVoxel.x += faceVector.x * ::mouseVoxel.s;
1846 ::mouseVoxel.y += faceVector.y * ::mouseVoxel.s;
1847 ::mouseVoxel.z += faceVector.z * ::mouseVoxel.s;
1848 }
1849 }
1850
1851 if (::mouseMode == COLOR_VOXEL_MODE) {
1852 ::mouseVoxel.red = 0;
1853 ::mouseVoxel.green = 255;
1854 ::mouseVoxel.blue = 0;
1855
1856 } else if (::mouseMode == DELETE_VOXEL_MODE) {
1857 // red indicates deletion
1858 ::mouseVoxel.red = 255;
1859 ::mouseVoxel.green = ::mouseVoxel.blue = 0;
1860 }
1861 }
1862
1863 // walking triggers the handControl to stop
1864 if (myAvatar.getMode() == AVATAR_MODE_WALKING) {
1865 handControl.stop();
1866 mouseViewShiftYaw *= 0.9;
1867 mouseViewShiftPitch *= 0.9;
1868 }
1869
1870 // Read serial port interface devices
1871 if (serialPort.active) {
1872 serialPort.readData();
1873 }
1874
1875 // Sample hardware, update view frustum if needed, and send avatar data to mixer/agents
1876 updateAvatar(deltaTime);
1877
1878 // read incoming packets from network
1879 if (!enableNetworkThread) {
1880 networkReceive(0);
1881 }
1882
1883 //loop through all the remote avatars and simulate them...
1884 AgentList* agentList = AgentList::getInstance();
1885 agentList->lock();
1886 for(AgentList::iterator agent = agentList->begin(); agent != agentList->end(); agent++) {
1887 if (agent->getLinkedData() != NULL) {
1888 Avatar *avatar = (Avatar *)agent->getLinkedData();
1889 avatar->simulate(deltaTime);
1890 }
1891 }
1892 agentList->unlock();
1893
1894 myAvatar.setGravity(getGravity(myAvatar.getPosition()));
1895 myAvatar.simulate(deltaTime);
1896
1897 // Update audio stats for procedural sounds
1898 audio.setLastAcceleration(myAvatar.getThrust());
1899 audio.setLastVelocity(myAvatar.getVelocity());
1900
1901 glutPostRedisplay();
1902 lastTimeIdle = check;
1903 }
1904}
1905
1906void reshape(int width, int height) {
1907 ::screenWidth = width;
1908 ::screenHeight = height;
1909 aspectRatio = ((float)width/(float)height); // based on screen resize
1910
1911 // get the lens details from the current camera
1912 Camera& camera = ::viewFrustumFromOffset ? (::viewFrustumOffsetCamera) : (::myCamera);
1913 float nearClip = camera.getNearClip();
1914 float farClip = camera.getFarClip();
1915 float fov;
1916
1917 if (::oculusOn) {
1918 // more magic numbers; see Oculus SDK docs, p. 32
1919 camera.setAspectRatio(aspectRatio *= 0.5);
1920 camera.setFieldOfView(fov = 2 * atan((0.0468 * ::oculusDistortionScale) / 0.041) * (180 / PI));
1921
1922 // resize the render texture
1923 if (::oculusTextureID != 0) {
1924 glBindTexture(GL_TEXTURE_2D, ::oculusTextureID);
1925 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, ::screenWidth, ::screenHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
1926 glBindTexture(GL_TEXTURE_2D, 0);
1927 }
1928 } else {
1929 camera.setAspectRatio(aspectRatio);
1930 camera.setFieldOfView(fov = 60);
1931 }
1932
1933 // Tell our viewFrustum about this change
1934 ::viewFrustum.setAspectRatio(aspectRatio);
1935
1936 glViewport(0, 0, width, height); // shouldn't this account for the menu???
1937
1938 glMatrixMode(GL_PROJECTION);
1939 glLoadIdentity();
1940
1941 // XXXBHG - If we're in view frustum mode, then we need to do this little bit of hackery so that
1942 // OpenGL won't clip our frustum rendering lines. This is a debug hack for sure! Basically, this makes
1943 // the near clip a little bit closer (therefor you see more) and the far clip a little bit farther (also,
1944 // to see more.)
1945 if (::frustumOn) {
1946 nearClip -= 0.01f;
1947 farClip += 0.01f;
1948 }
1949
1950 // On window reshape, we need to tell OpenGL about our new setting
1951 gluPerspective(fov,aspectRatio,nearClip,farClip);
1952
1953 glMatrixMode(GL_MODELVIEW);
1954 glLoadIdentity();
1955}
1956
1957//Find and return the gravity vector at this location
1958glm::vec3 getGravity(glm::vec3 pos) {
1959 //
1960 // For now, we'll test this with a simple global lookup, but soon we will add getting this
1961 // from the domain/voxelserver (or something similar)
1962 //
1963 if ((pos.x > 0.f) &&
1964 (pos.x < 10.f) &&
1965 (pos.z > 0.f) &&
1966 (pos.z < 10.f) &&
1967 (pos.y > 0.f) &&
1968 (pos.y < 3.f)) {
1969 // If above ground plane, turn gravity on
1970 return glm::vec3(0.f, -1.f, 0.f);
1971 } else {
1972 // If flying in space, turn gravity OFF
1973 return glm::vec3(0.f, 0.f, 0.f);
1974 }
1975}
1976
1977bool menuDisplayed = false;
1978void mouseFunc(int button, int state, int x, int y) {
1979 bool menuFound = menu.mouseClick(x, y);
1980
1981 // If we didn't previously have the menu displayed, and we did just click on the menu, then
1982 // go into menuDisplayed mode....
1983 if (!::menuDisplayed && menuFound) {
1984 ::menuDisplayed = true;
1985 }
1986
1987 // If the menu was displayed, and we're not over a menu, then leave menu mode
1988 if (::menuDisplayed && !menuFound) {
1989 ::menuDisplayed = false;
1990 menu.hidePopupMenu();
1991 }
1992
1993 // In menu displayed mode use old logic
1994 if (::menuDisplayed) {
1995 if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN) {
1996 if (state == GLUT_DOWN && !menu.mouseClick(x, y)) {
1997 mouseX = x;
1998 mouseY = y;
1999 mousePressed = 1;
2000 } else if (state == GLUT_UP) {
2001 mouseX = x;
2002 mouseY = y;
2003 mousePressed = 0;
2004 }
2005 }
2006 } else {
2007 if (button == GLUT_LEFT_BUTTON) {
2008 mouseX = x;
2009 mouseY = y;
2010
2011 if (state == GLUT_DOWN) {
2012 mousePressed = 1;
2013 if (::mouseMode == ADD_VOXEL_MODE || ::mouseMode == COLOR_VOXEL_MODE) {
2014 addVoxelUnderCursor();
2015
2016 } else if (::mouseMode == DELETE_VOXEL_MODE) {
2017 deleteVoxelUnderCursor();
2018 }
2019 } else if (state == GLUT_UP) {
2020 mousePressed = 0;
2021 }
2022 } else if (button == GLUT_RIGHT_BUTTON && state == GLUT_DOWN && ::mouseMode != NO_EDIT_MODE) {
2023 deleteVoxelUnderCursor();
2024 }
2025 }
2026}
2027
2028
2029void motionFunc(int x, int y) {
2030 mouseX = x;
2031 mouseY = y;
2032}
2033
2034void mouseoverFunc(int x, int y){
2035 menu.mouseOver(x, y);
2036
2037 mouseX = x;
2038 mouseY = y;
2039}
2040
2041void attachNewHeadToAgent(Agent *newAgent) {
2042 if (newAgent->getLinkedData() == NULL) {
2043 newAgent->setLinkedData(new Avatar(false));
2044 }
2045}
2046
2047#ifndef _WIN32
2048void audioMixerUpdate(in_addr_t newMixerAddress, in_port_t newMixerPort) {
2049 audio.updateMixerParams(newMixerAddress, newMixerPort);
2050}
2051#endif
2052 20
2053int main(int argc, const char * argv[]) {21int main(int argc, const char * argv[]) {
2054 22
@@ -2057,124 +25,4 @@ int main(int argc, const char * argv[]) { · 2 unmodified lines
2057 int exitCode = app.exec();25 int exitCode = app.exec();
2058 printLog("Normal exit.\n");26 printLog("Normal exit.\n");
2059 return exitCode;27 return exitCode;
2060
2061
2062
2063 gettimeofday(&applicationStartupTime, NULL);
2064 printLog("Interface Startup:\n");
2065
2066 voxels.setViewFrustum(&::viewFrustum);
2067
2068 shared_lib::printLog = & ::printLog;
2069 voxels_lib::printLog = & ::printLog;
2070 avatars_lib::printLog = & ::printLog;
2071
2072 unsigned int listenPort = AGENT_SOCKET_LISTEN_PORT;
2073 const char* portStr = getCmdOption(argc, argv, "--listenPort");
2074 if (portStr) {
2075 listenPort = atoi(portStr);
2076 }
2077 AgentList::createInstance(AGENT_TYPE_AVATAR, listenPort);
2078 enableNetworkThread = !cmdOptionExists(argc, argv, "--nonblocking");
2079 if (!enableNetworkThread) {
2080 AgentList::getInstance()->getAgentSocket().setBlocking(false);
2081 }
2082
2083 const char* domainIP = getCmdOption(argc, argv, "--domain");
2084 if (domainIP) {
2085 strcpy(DOMAIN_IP,domainIP);
2086 }
2087
2088 // Handle Local Domain testing with the --local command line
2089 if (cmdOptionExists(argc, argv, "--local")) {
2090 printLog("Local Domain MODE!\n");
2091 int ip = getLocalAddress();
2092 sprintf(DOMAIN_IP,"%d.%d.%d.%d", (ip & 0xFF), ((ip >> 8) & 0xFF),((ip >> 16) & 0xFF), ((ip >> 24) & 0xFF));
2093 }
2094
2095 // the callback for our instance of AgentList is attachNewHeadToAgent
2096 AgentList::getInstance()->linkedDataCreateCallback = &attachNewHeadToAgent;
2097
2098 #ifndef _WIN32
2099 AgentList::getInstance()->audioMixerSocketUpdate = &audioMixerUpdate;
2100 #endif
2101
2102#ifdef _WIN32
2103 WSADATA WsaData;
2104 int wsaresult = WSAStartup(MAKEWORD(2,2), &WsaData);
2105#endif
2106
2107 // start the agentList threads
2108 AgentList::getInstance()->startSilentAgentRemovalThread();
2109 AgentList::getInstance()->startDomainServerCheckInThread();
2110 AgentList::getInstance()->startPingUnknownAgentsThread();
2111
2112 glutInit(&argc, (char**)argv);
2113 ::screenWidth = glutGet(GLUT_SCREEN_WIDTH);
2114 ::screenHeight = glutGet(GLUT_SCREEN_HEIGHT);
2115
2116 glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH);
2117 glutInitWindowSize(::screenWidth, ::screenHeight);
2118 glutCreateWindow("Interface");
2119 printLog( "Created Display Window.\n" );
2120
2121 #ifdef _WIN32
2122 glewInit();
2123 printLog( "Glew Init complete.\n" );
2124
2125 #endif
2126
2127 // Before we render anything, let's set up our viewFrustumOffsetCamera with a sufficiently large
2128 // field of view and near and far clip to make it interesting.
2129 //viewFrustumOffsetCamera.setFieldOfView(90.0);
2130 viewFrustumOffsetCamera.setNearClip(0.1);
2131 viewFrustumOffsetCamera.setFarClip(500.0*TREE_SCALE);
2132
2133
2134 initMenu();
2135 initDisplay();
2136
2137 glutDisplayFunc(display);
2138 glutReshapeFunc(reshape);
2139 glutKeyboardFunc(key);
2140 glutKeyboardUpFunc(keyUp);
2141 glutSpecialFunc(specialkey);
2142 glutSpecialUpFunc(specialkeyUp);
2143 glutMotionFunc(motionFunc);
2144 glutPassiveMotionFunc(mouseoverFunc);
2145 glutMouseFunc(mouseFunc);
2146 glutIdleFunc(idle);
2147 printLog( "Initialized Display.\n" );
2148
2149
2150 init();
2151 printLog( "Init() complete.\n" );
2152
2153 // Check to see if the user passed in a command line option for randomizing colors
2154 if (cmdOptionExists(argc, argv, "--NoColorRandomizer")) {
2155 wantColorRandomizer = false;
2156 }
2157
2158 // Check to see if the user passed in a command line option for loading a local
2159 // Voxel File. If so, load it now.
2160 const char* voxelsFilename = getCmdOption(argc, argv, "-i");
2161 if (voxelsFilename) {
2162 voxels.loadVoxelsFile(voxelsFilename,wantColorRandomizer);
2163 printLog("Local Voxel File loaded.\n");
2164 }
2165
2166 // create thread for receipt of data via UDP
2167 if (enableNetworkThread) {
2168 pthread_create(&networkReceiveThread, NULL, networkReceive, NULL);
2169 printLog("Network receive thread created.\n");
2170 }
2171
2172 myAvatar.readAvatarDataFromFile();
2173
2174 glutTimerFunc(1000, Timer, 0);
2175 glutMainLoop();
2176
2177 printLog("Normal exit.\n");
2178 ::terminate();
2179 return EXIT_SUCCESS;
2180} 28}

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t