summaryrefslogtreecommitdiffstats
path: root/Samples/OculusRoomTiny
diff options
context:
space:
mode:
authorBrad Davis <[email protected]>2013-06-27 11:25:32 -0800
committerBrad Davis <[email protected]>2013-06-28 10:47:29 -0700
commitebefcc885f74461cd0e3f19b5ae3622dc6cf6dbc (patch)
tree2b16db7350fce54c2e6c1b1c4020d67419cb1164 /Samples/OculusRoomTiny
parent0ade748e1845694c5cbe562fb823e56f09773e27 (diff)
SDK 0.2.2
Diffstat (limited to 'Samples/OculusRoomTiny')
-rw-r--r--Samples/OculusRoomTiny/OSX_OculusRoomTiny.h223
-rw-r--r--Samples/OculusRoomTiny/OSX_OculusRoomTiny.mm861
-rw-r--r--Samples/OculusRoomTiny/OculusRoomModel.cpp260
-rw-r--r--Samples/OculusRoomTiny/OculusRoomTiny.rcbin0 -> 144 bytes
-rw-r--r--Samples/OculusRoomTiny/OculusRoomTiny_Msvc2010.vcxproj187
-rw-r--r--Samples/OculusRoomTiny/OculusRoomTiny_Msvc2010.vcxproj.filters17
-rw-r--r--Samples/OculusRoomTiny/RenderTiny_D3D1X_Device.cpp1311
-rw-r--r--Samples/OculusRoomTiny/RenderTiny_D3D1X_Device.h273
-rw-r--r--Samples/OculusRoomTiny/RenderTiny_Device.cpp442
-rw-r--r--Samples/OculusRoomTiny/RenderTiny_Device.h724
-rw-r--r--Samples/OculusRoomTiny/RenderTiny_GL_Device.cpp784
-rw-r--r--Samples/OculusRoomTiny/RenderTiny_GL_Device.h228
-rw-r--r--Samples/OculusRoomTiny/Win32_OculusRoomTiny.cpp712
-rw-r--r--Samples/OculusRoomTiny/Win32_OculusRoomTiny.h189
14 files changed, 6211 insertions, 0 deletions
diff --git a/Samples/OculusRoomTiny/OSX_OculusRoomTiny.h b/Samples/OculusRoomTiny/OSX_OculusRoomTiny.h
new file mode 100644
index 0000000..dc51509
--- /dev/null
+++ b/Samples/OculusRoomTiny/OSX_OculusRoomTiny.h
@@ -0,0 +1,223 @@
+/************************************************************************************
+
+ Filename : OSX_OculusRoomTiny.h
+ Content : Simplest possible first-person view test application for Oculus Rift
+ Created : May 7, 2013
+ Authors : Michael Antonov, Andrew Reisse, Artem Bolgar
+
+ Copyright : Copyright 2013 Oculus, Inc. All Rights reserved.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+ *************************************************************************************/
+#ifndef INC_OSX_OculusRoomTiny_h
+#define INC_OSX_OculusRoomTiny_h
+
+#import <Cocoa/Cocoa.h>
+
+#import <CoreGraphics/CoreGraphics.h>
+#import <CoreGraphics/CGDirectDisplay.h>
+
+
+#include "OVR.h"
+#include "Util/Util_Render_Stereo.h"
+#include "../../LibOVR/Src/Kernel/OVR_Timer.h"
+#include "RenderTiny_GL_Device.h"
+
+using namespace OVR;
+using namespace OVR::RenderTiny;
+
+class OculusRoomTinyApp;
+
+@interface OVRApp : NSApplication
+
+@property (assign) IBOutlet NSWindow* win;
+@property (assign) OculusRoomTinyApp* App;
+
+-(void) run;
+
+@end
+
+@interface OVRView : NSOpenGLView <NSWindowDelegate>
+
+//@property (assign) OVR::Platform::OSX::PlatformCore* Platform;
+@property (assign) OculusRoomTinyApp* App;
+@property unsigned long Modifiers;
+
+-(void)ProcessMouse:(NSEvent*)event;
+-(void)warpMouseToCenter;
+
++(CGDirectDisplayID) displayFromScreen:(NSScreen*)s;
+
+@end
+
+//-------------------------------------------------------------------------------------
+// ***** OculusRoomTiny Description
+
+// This app renders a simple flat-shaded room allowing the user to move along the
+// floor and look around with an HMD, mouse, keyboard and gamepad.
+// By default, the application will start full-screen on Oculus Rift.
+//
+// The following keys work:
+//
+// 'W', 'S', 'A', 'D' - Move forward, back; strafe left/right.
+// F1 - No stereo, no distortion.
+// F2 - Stereo, no distortion.
+// F3 - Stereo and distortion.
+//
+
+// The world RHS coordinate system is defines as follows (as seen in perspective view):
+// Y - Up
+// Z - Back
+// X - Right
+const Vector3f UpVector(0.0f, 1.0f, 0.0f);
+const Vector3f ForwardVector(0.0f, 0.0f, -1.0f);
+const Vector3f RightVector(1.0f, 0.0f, 0.0f);
+
+// We start out looking in the positive Z (180 degree rotation).
+const float YawInitial = 3.141592f;
+const float Sensitivity = 1.0f;
+const float MoveSpeed = 3.0f; // m/s
+
+namespace OSX
+{
+ class RenderDevice : public GL::RenderDevice
+ {
+ public:
+ void* Context; // NSOpenGLContext
+
+ // osview = NSView*
+ RenderDevice(const RendererParams& p, void* osview, void* context);
+
+ virtual void Shutdown();
+ virtual void Present();
+
+ virtual bool SetFullscreen(DisplayMode fullscreen);
+
+ // osview = NSView*
+ static RenderDevice* CreateDevice(const RendererParams& rp, void* osview);
+ };
+}
+
+//-------------------------------------------------------------------------------------
+// ***** OculusRoomTiny Application class
+
+// An instance of this class is created on application startup (main/WinMain).
+//
+// It then works as follows:
+//
+// OnStartup - Window, graphics and HMD setup is done here.
+// This function will initialize OVR::DeviceManager and HMD,
+// creating SensorDevice and attaching it to SensorFusion.
+// This needs to be done before obtaining sensor data.
+//
+// OnIdle - Does per-frame processing, processing SensorFusion and
+// movement input and rendering the frame.
+
+class OculusRoomTinyApp : public MessageHandler
+{
+ friend class OSX::RenderDevice;
+public:
+ OculusRoomTinyApp(OVRApp* nsapp);
+ ~OculusRoomTinyApp();
+
+ // Initializes graphics, Rift input and creates world model.
+ virtual int OnStartup(const char* args);
+ // Called per frame to sample SensorFucion and render the world.
+ virtual void OnIdle();
+
+ // Installed for Oculus device messages. Optional.
+ virtual void OnMessage(const Message& msg);
+
+ // Handle input events for movement.
+ virtual void OnMouseMove(int x, int y, int modifiers);
+ virtual void OnKey(unsigned vk, bool down);
+
+ // Render the view for one eye.
+ void Render(const StereoEyeParams& stereo);
+
+ // Main application loop.
+ int Run();
+ void Exit();
+
+ // Return amount of time passed since application started in seconds.
+ double GetAppTime() const
+ {
+ return (OVR::Timer::GetTicks() - StartupTicks) * (1.0 / (double)OVR::Timer::MksPerSecond);
+ }
+ bool IsQuiting() const { return Quit; }
+
+ int GetWidth() const { return Width; }
+ int GetHeight() const { return Height; }
+
+ bool SetFullscreen(const RendererParams& rp, int fullscreen);
+
+protected:
+ bool setupWindow();
+ void destroyWindow();
+
+ NSView* View;
+ NSWindow* Win;
+ OVRApp* NsApp;
+
+ static OculusRoomTinyApp* pApp;
+
+ // *** Rendering Variables
+ Ptr<OSX::RenderDevice> pRender;
+ RendererParams RenderParams;
+ int Width, Height;
+
+ bool Quit;
+
+ // *** Oculus HMD Variables
+
+ Ptr<DeviceManager> pManager;
+ Ptr<SensorDevice> pSensor;
+ Ptr<HMDDevice> pHMD;
+ SensorFusion SFusion;
+ OVR::HMDInfo HMDInfo;
+
+ // Last update seconds, used for move speed timing.
+ double LastUpdate;
+ OVR::UInt64 StartupTicks;
+
+ // Position and look. The following apply:
+ Vector3f EyePos;
+ float EyeYaw; // Rotation around Y, CCW positive when looking at RHS (X,Z) plane.
+ float EyePitch; // Pitch. If sensor is plugged in, only read from sensor.
+ float EyeRoll; // Roll, only accessible from Sensor.
+ float LastSensorYaw; // Stores previous Yaw value from to support computing delta.
+
+ // Movement state; different bits may be set based on the state of keys.
+ UByte MoveForward;
+ UByte MoveBack;
+ UByte MoveLeft;
+ UByte MoveRight;
+
+ Matrix4f ViewMat;
+ RenderTiny::Scene Scene;
+
+ // Stereo view parameters.
+ StereoConfig SConfig;
+ PostProcessType PostProcess;
+
+ // Shift accelerates movement/adjustment velocity.
+ bool ShiftDown;
+ bool ControlDown;
+};
+
+// Adds sample models and lights to the argument scene.
+void PopulateRoomScene(Scene* scene, RenderDevice* render);
+
+
+#endif
diff --git a/Samples/OculusRoomTiny/OSX_OculusRoomTiny.mm b/Samples/OculusRoomTiny/OSX_OculusRoomTiny.mm
new file mode 100644
index 0000000..0a59afd
--- /dev/null
+++ b/Samples/OculusRoomTiny/OSX_OculusRoomTiny.mm
@@ -0,0 +1,861 @@
+/************************************************************************************
+
+ Filename : OSX_OculusRoomTiny.mm
+ Content : Simplest possible first-person view test application for Oculus Rift
+ Created : May 7, 2013
+ Authors : Michael Antonov, Andrew Reisse, Artem Bolgar
+
+ Copyright : Copyright 2013 Oculus, Inc. All Rights reserved.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+ *************************************************************************************/
+
+#import "OSX_OculusRoomTiny.h"
+#include "RenderTiny_GL_Device.h"
+
+#include "Kernel/OVR_KeyCodes.h"
+
+using namespace OVR;
+
+//-------------------------------------------------------------------------------------
+// ***** OculusRoomTiny Class
+
+// Static pApp simplifies routing the window function.
+OculusRoomTinyApp* OculusRoomTinyApp::pApp = 0;
+
+
+OculusRoomTinyApp::OculusRoomTinyApp(OVRApp* nsapp)
+ : pRender(0),
+ LastUpdate(0),
+ NsApp(nsapp),
+ Quit(0),
+
+ // Initial location
+ EyePos(0.0f, 1.6f, -5.0f),
+ EyeYaw(YawInitial), EyePitch(0), EyeRoll(0),
+ LastSensorYaw(0),
+ SConfig(),
+ PostProcess(PostProcess_Distortion),
+ ShiftDown(false),
+ ControlDown(false)
+{
+ pApp = this;
+
+ Width = 1280;
+ Height = 800;
+
+ StartupTicks = OVR::Timer::GetTicks();
+
+ MoveForward = MoveBack = MoveLeft = MoveRight = 0;
+}
+
+OculusRoomTinyApp::~OculusRoomTinyApp()
+{
+ RemoveHandlerFromDevices();
+ pSensor.Clear();
+ pHMD.Clear();
+ destroyWindow();
+ pApp = 0;
+}
+
+
+int OculusRoomTinyApp::OnStartup(const char* args)
+{
+ OVR_UNUSED(args);
+
+
+ // *** Oculus HMD & Sensor Initialization
+
+ // Create DeviceManager and first available HMDDevice from it.
+ // Sensor object is created from the HMD, to ensure that it is on the
+ // correct device.
+
+ pManager = *DeviceManager::Create();
+
+ // We'll handle it's messages in this case.
+ pManager->SetMessageHandler(this);
+
+ CFOptionFlags detectionResult;
+ const char* detectionMessage;
+
+ do
+ {
+ // Release Sensor/HMD in case this is a retry.
+ pSensor.Clear();
+ pHMD.Clear();
+ RenderParams.MonitorName.Clear();
+
+ pHMD = *pManager->EnumerateDevices<HMDDevice>().CreateDevice();
+ if (pHMD)
+ {
+ pSensor = *pHMD->GetSensor();
+
+ // This will initialize HMDInfo with information about configured IPD,
+ // screen size and other variables needed for correct projection.
+ // We pass HMD DisplayDeviceName into the renderer to select the
+ // correct monitor in full-screen mode.
+ if (pHMD->GetDeviceInfo(&HMDInfo))
+ {
+ RenderParams.MonitorName = HMDInfo.DisplayDeviceName;
+ RenderParams.DisplayId = HMDInfo.DisplayId;
+ SConfig.SetHMDInfo(HMDInfo);
+ }
+ }
+ else
+ {
+ // If we didn't detect an HMD, try to create the sensor directly.
+ // This is useful for debugging sensor interaction; it is not needed in
+ // a shipping app.
+ pSensor = *pManager->EnumerateDevices<SensorDevice>().CreateDevice();
+ }
+
+
+ // If there was a problem detecting the Rift, display appropriate message.
+ detectionResult = kCFUserNotificationAlternateResponse;
+
+ if (!pHMD && !pSensor)
+ detectionMessage = "Oculus Rift not detected.";
+ else if (!pHMD)
+ detectionMessage = "Oculus Sensor detected; HMD Display not detected.";
+ else if (!pSensor)
+ detectionMessage = "Oculus HMD Display detected; Sensor not detected.";
+ else if (HMDInfo.DisplayDeviceName[0] == '\0')
+ detectionMessage = "Oculus Sensor detected; HMD display EDID not detected.";
+ else
+ detectionMessage = 0;
+
+ if (detectionMessage)
+ {
+ String messageText(detectionMessage);
+ messageText += "\n\n"
+ "Press 'Try Again' to run retry detection.\n"
+ "Press 'Continue' to run full-screen anyway.";
+
+ CFStringRef headerStrRef = CFStringCreateWithCString(NULL, "Oculus Rift Detection", kCFStringEncodingMacRoman);
+ CFStringRef messageStrRef = CFStringCreateWithCString(NULL, messageText, kCFStringEncodingMacRoman);
+
+ //launch the message box
+ CFUserNotificationDisplayAlert(0,
+ kCFUserNotificationNoteAlertLevel,
+ NULL, NULL, NULL,
+ headerStrRef, // header text
+ messageStrRef, // message text
+ CFSTR("Try again"),
+ CFSTR("Continue"),
+ CFSTR("Cancel"),
+ &detectionResult);
+
+ //Clean up the strings
+ CFRelease(headerStrRef);
+ CFRelease(messageStrRef);
+
+ if (detectionResult == kCFUserNotificationCancelResponse ||
+ detectionResult == kCFUserNotificationOtherResponse)
+ return 1;
+ }
+
+ } while (detectionResult != kCFUserNotificationAlternateResponse);
+
+
+ if (HMDInfo.HResolution > 0)
+ {
+ Width = HMDInfo.HResolution;
+ Height = HMDInfo.VResolution;
+ }
+
+
+ if (!setupWindow())
+ return 1;
+
+ if (pSensor)
+ {
+ // We need to attach sensor to SensorFusion object for it to receive
+ // body frame messages and update orientation. SFusion.GetOrientation()
+ // is used in OnIdle() to orient the view.
+ SFusion.AttachToSensor(pSensor);
+ SFusion.SetDelegateMessageHandler(this);
+ SFusion.SetPredictionEnabled(true);
+ }
+
+
+ // *** Initialize Rendering
+
+ // Enable multi-sampling by default.
+ RenderParams.Multisample = 4;
+ RenderParams.Fullscreen = false;//true; //?
+
+ // Setup Graphics.
+ pRender = *OSX::RenderDevice::CreateDevice(RenderParams, (void*)View);
+ if (!pRender)
+ return 1;
+
+
+ // *** Configure Stereo settings.
+
+ SConfig.SetFullViewport(Viewport(0,0, Width, Height));
+ SConfig.SetStereoMode(Stereo_LeftRight_Multipass);
+
+ // Configure proper Distortion Fit.
+ // For 7" screen, fit to touch left side of the view, leaving a bit of invisible
+ // screen on the top (saves on rendering cost).
+ // For smaller screens (5.5"), fit to the top.
+ if (HMDInfo.HScreenSize > 0.0f)
+ {
+ if (HMDInfo.HScreenSize > 0.140f) // 7"
+ SConfig.SetDistortionFitPointVP(-1.0f, 0.0f);
+ else
+ SConfig.SetDistortionFitPointVP(0.0f, 1.0f);
+ }
+
+ pRender->SetSceneRenderScale(SConfig.GetDistortionScale());
+
+ SConfig.Set2DAreaFov(DegreeToRad(85.0f));
+
+
+ // *** Populate Room Scene
+
+ // This creates lights and models.
+ PopulateRoomScene(&Scene, pRender);
+
+
+ LastUpdate = GetAppTime();
+ return 0;
+}
+
+void OculusRoomTinyApp::OnMessage(const Message& msg)
+{
+ if (msg.Type == Message_DeviceAdded && msg.pDevice == pManager)
+ {
+ LogText("DeviceManager reported device added.\n");
+ }
+ else if (msg.Type == Message_DeviceRemoved && msg.pDevice == pManager)
+ {
+ LogText("DeviceManager reported device removed.\n");
+ }
+ else if (msg.Type == Message_DeviceAdded && msg.pDevice == pSensor)
+ {
+ LogText("Sensor reported device added.\n");
+ }
+ else if (msg.Type == Message_DeviceRemoved && msg.pDevice == pSensor)
+ {
+ LogText("Sensor reported device removed.\n");
+ }
+}
+
+bool OculusRoomTinyApp::setupWindow()
+{
+ NSRect winrect;
+ winrect.origin.x = 0;
+ winrect.origin.y = 1000;
+ winrect.size.width = Width;
+ winrect.size.height = Height;
+ NSWindow* win = [[NSWindow alloc] initWithContentRect:winrect styleMask:NSTitledWindowMask|NSClosableWindowMask backing:NSBackingStoreBuffered defer:NO];
+
+ OVRView* view = [[OVRView alloc] initWithFrame:winrect];
+ [win setContentView:view];
+ [win setAcceptsMouseMovedEvents:YES];
+ [win setDelegate:view];
+ [view setApp:pApp];
+ Win = win;
+ View = view;
+
+ const char* title = "OculusRoomTiny";
+ [((NSWindow*)Win) setTitle:[[NSString alloc] initWithBytes:title length:strlen(title) encoding:NSUTF8StringEncoding]];
+
+ [NSCursor hide];
+ [view warpMouseToCenter];
+ CGAssociateMouseAndMouseCursorPosition(false);
+
+ SetFullscreen(RenderParams, true);
+ return true;
+}
+
+void OculusRoomTinyApp::destroyWindow()
+{
+ SetFullscreen(RenderParams, false);
+ [((NSWindow*)Win) close];
+}
+
+void OculusRoomTinyApp::OnMouseMove(int x, int y, int modifiers)
+{
+ OVR_UNUSED(modifiers);
+
+ // Mouse motion here is always relative.
+ int dx = x, dy = y;
+ const float maxPitch = ((3.1415f/2)*0.98f);
+
+ // Apply to rotation. Subtract for right body frame rotation,
+ // since yaw rotation is positive CCW when looking down on XZ plane.
+ EyeYaw -= (Sensitivity * dx)/ 360.0f;
+
+ if (!pSensor)
+ {
+ EyePitch -= (Sensitivity * dy)/ 360.0f;
+
+ if (EyePitch > maxPitch)
+ EyePitch = maxPitch;
+ if (EyePitch < -maxPitch)
+ EyePitch = -maxPitch;
+ }
+}
+
+
+void OculusRoomTinyApp::OnKey(unsigned vk, bool down)
+{
+ switch (vk)
+ {
+ case 'Q':
+ if (down && ControlDown)
+ Exit();;
+ break;
+ case Key_Escape:
+ if (!down)
+ Exit();
+ break;
+
+ // Handle player movement keys.
+ // We just update movement state here, while the actual translation is done in OnIdle()
+ // based on time.
+ case 'W': MoveForward = down ? (MoveForward | 1) : (MoveForward & ~1); break;
+ case 'S': MoveBack = down ? (MoveBack | 1) : (MoveBack & ~1); break;
+ case 'A': MoveLeft = down ? (MoveLeft | 1) : (MoveLeft & ~1); break;
+ case 'D': MoveRight = down ? (MoveRight | 1) : (MoveRight & ~1); break;
+ case Key_Up: MoveForward = down ? (MoveForward | 2) : (MoveForward & ~2); break;
+ case Key_Down: MoveBack = down ? (MoveBack | 2) : (MoveBack & ~2); break;
+
+ case 'R':
+ SFusion.Reset();
+ break;
+
+ case 'P':
+ if (down)
+ {
+ // Toggle chromatic aberration correction on/off.
+ RenderDevice::PostProcessShader shader = pRender->GetPostProcessShader();
+
+ if (shader == RenderDevice::PostProcessShader_Distortion)
+ {
+ pRender->SetPostProcessShader(RenderDevice::PostProcessShader_DistortionAndChromAb);
+ }
+ else if (shader == RenderDevice::PostProcessShader_DistortionAndChromAb)
+ {
+ pRender->SetPostProcessShader(RenderDevice::PostProcessShader_Distortion);
+ }
+ else
+ OVR_ASSERT(false);
+ }
+ break;
+
+ // Switch rendering modes/distortion.
+ case Key_F1:
+ SConfig.SetStereoMode(Stereo_None);
+ PostProcess = PostProcess_None;
+ break;
+ case Key_F2:
+ SConfig.SetStereoMode(Stereo_LeftRight_Multipass);
+ PostProcess = PostProcess_None;
+ break;
+ case Key_F3:
+ SConfig.SetStereoMode(Stereo_LeftRight_Multipass);
+ PostProcess = PostProcess_Distortion;
+ break;
+
+ // Stereo IPD adjustments, in meter (default IPD is 64mm).
+ case '+':
+ case '=':
+ if (down)
+ SConfig.SetIPD(SConfig.GetIPD() + 0.0005f * (ShiftDown ? 5.0f : 1.0f));
+ break;
+ case '-':
+ case '_':
+ if (down)
+ SConfig.SetIPD(SConfig.GetIPD() - 0.0005f * (ShiftDown ? 5.0f : 1.0f));
+ break;
+
+ // Holding down Shift key accelerates adjustment velocity.
+ case Key_Shift:
+ ShiftDown = down;
+ break;
+ case Key_Meta:
+ ControlDown = down;
+ break;
+ }
+}
+
+
+void OculusRoomTinyApp::OnIdle()
+{
+ double curtime = GetAppTime();
+ float dt = float(curtime - LastUpdate);
+ LastUpdate = curtime;
+
+
+ // Handle Sensor motion.
+ // We extract Yaw, Pitch, Roll instead of directly using the orientation
+ // to allow "additional" yaw manipulation with mouse/controller.
+ if (pSensor)
+ {
+ Quatf hmdOrient = SFusion.GetOrientation();
+ float yaw = 0.0f;
+
+ hmdOrient.GetEulerAngles<Axis_Y, Axis_X, Axis_Z>(&yaw, &EyePitch, &EyeRoll);
+
+ EyeYaw += (yaw - LastSensorYaw);
+ LastSensorYaw = yaw;
+ }
+
+
+ if (!pSensor)
+ {
+ const float maxPitch = ((3.1415f/2)*0.98f);
+ if (EyePitch > maxPitch)
+ EyePitch = maxPitch;
+ if (EyePitch < -maxPitch)
+ EyePitch = -maxPitch;
+ }
+
+ // Handle keyboard movement.
+ // This translates EyePos based on Yaw vector direction and keys pressed.
+ // Note that Pitch and Roll do not affect movement (they only affect view).
+ if (MoveForward || MoveBack || MoveLeft || MoveRight)
+ {
+ Vector3f localMoveVector(0,0,0);
+ Matrix4f yawRotate = Matrix4f::RotationY(EyeYaw);
+
+ if (MoveForward)
+ localMoveVector = ForwardVector;
+ else if (MoveBack)
+ localMoveVector = -ForwardVector;
+
+ if (MoveRight)
+ localMoveVector += RightVector;
+ else if (MoveLeft)
+ localMoveVector -= RightVector;
+
+ // Normalize vector so we don't move faster diagonally.
+ localMoveVector.Normalize();
+ Vector3f orientationVector = yawRotate.Transform(localMoveVector);
+ orientationVector *= MoveSpeed * dt * (ShiftDown ? 3.0f : 1.0f);
+
+ EyePos += orientationVector;
+ }
+
+ // Rotate and position View Camera, using YawPitchRoll in BodyFrame coordinates.
+ //
+ Matrix4f rollPitchYaw = Matrix4f::RotationY(EyeYaw) * Matrix4f::RotationX(EyePitch) *
+ Matrix4f::RotationZ(EyeRoll);
+ Vector3f up = rollPitchYaw.Transform(UpVector);
+ Vector3f forward = rollPitchYaw.Transform(ForwardVector);
+
+
+ // Minimal head modelling.
+ float headBaseToEyeHeight = 0.15f; // Vertical height of eye from base of head
+ float headBaseToEyeProtrusion = 0.09f; // Distance forward of eye from base of head
+
+ Vector3f eyeCenterInHeadFrame(0.0f, headBaseToEyeHeight, -headBaseToEyeProtrusion);
+ Vector3f shiftedEyePos = EyePos + rollPitchYaw.Transform(eyeCenterInHeadFrame);
+ shiftedEyePos.y -= eyeCenterInHeadFrame.y; // Bring the head back down to original height
+
+ ViewMat = Matrix4f::LookAtRH(shiftedEyePos, shiftedEyePos + forward, up);
+
+ // This is what transformation would be without head modeling.
+ // View = Matrix4f::LookAtRH(EyePos, EyePos + forward, up);
+
+ switch(SConfig.GetStereoMode())
+ {
+ case Stereo_None:
+ Render(SConfig.GetEyeRenderParams(StereoEye_Center));
+ break;
+
+ case Stereo_LeftRight_Multipass:
+ Render(SConfig.GetEyeRenderParams(StereoEye_Left));
+ Render(SConfig.GetEyeRenderParams(StereoEye_Right));
+ break;
+ }
+
+ pRender->Present();
+ // Force GPU to flush the scene, resulting in the lowest possible latency.
+ pRender->ForceFlushGPU();
+}
+
+
+// Render the scene for one eye.
+void OculusRoomTinyApp::Render(const StereoEyeParams& stereo)
+{
+ pRender->BeginScene(PostProcess);
+
+ // Apply Viewport/Projection for the eye.
+ pRender->ApplyStereoParams(stereo);
+ pRender->Clear();
+ pRender->SetDepthMode(true, true);
+
+ Scene.Render(pRender, stereo.ViewAdjust * ViewMat);
+
+ pRender->FinishScene();
+}
+
+void OculusRoomTinyApp::Exit()
+{
+ [NsApp stop:nil];
+ Quit = true;
+}
+
+bool OculusRoomTinyApp::SetFullscreen(const RenderTiny::RendererParams& rp, int fullscreen)
+{
+ if (fullscreen == RenderTiny::Display_Window)
+ [(OVRView*)View exitFullScreenModeWithOptions:nil];
+ else
+ {
+ NSScreen* usescreen = [NSScreen mainScreen];
+ NSArray* screens = [NSScreen screens];
+ for (int i = 0; i < [screens count]; i++)
+ {
+ NSScreen* s = (NSScreen*)[screens objectAtIndex:i];
+ CGDirectDisplayID disp = [OVRView displayFromScreen:s];
+
+ if (disp == rp.DisplayId)
+ usescreen = s;
+ }
+
+ [(OVRView*)View enterFullScreenMode:usescreen withOptions:nil];
+ }
+
+ if (pRender)
+ pRender->SetFullscreen((RenderTiny::DisplayMode)fullscreen);
+ return 1;
+}
+
+//-------------------------------------------------------------------------------------
+// ***** OS X-Specific Logic
+
+@implementation OVRApp
+
+- (void)dealloc
+{
+ [super dealloc];
+}
+
+- (void)run
+{
+ NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
+ _running = YES;
+ OculusRoomTinyApp* app;
+
+ // Initializes LibOVR. This LogMask_All enables maximum logging.
+ // Custom allocator can also be specified here.
+ OVR::System::Init(OVR::Log::ConfigureDefaultLog(OVR::LogMask_All));
+
+ int exitCode = 0;
+ do
+ {
+ {
+ using namespace OVR;
+
+ // CreateApplication must be the first call since it does OVR::System::Initialize.
+ app = new OculusRoomTinyApp(self);
+ // The platform attached to an app will be deleted by DestroyApplication.
+
+ [self setApp:app];
+
+ const char* argv[] = {"OVRApp"};
+ exitCode = app->OnStartup(argv[0]);
+ if (exitCode)
+ break;
+ }
+ [self finishLaunching];
+ [pool drain];
+
+ while ([self isRunning])
+ {
+ pool = [[NSAutoreleasePool alloc] init];
+ NSEvent* event = [self nextEventMatchingMask:NSAnyEventMask untilDate:nil inMode:NSDefaultRunLoopMode dequeue:YES];
+ if (event)
+ {
+ [self sendEvent:event];
+ }
+ _App->OnIdle();
+ [pool drain];
+ }
+ } while(0);
+
+ delete app;
+
+ // No OVR functions involving memory are allowed after this.
+ OVR::System::Destroy();
+}
+
+@end
+
+static int KeyMap[][2] =
+{
+ { NSDeleteFunctionKey, OVR::Key_Delete },
+ { '\t', OVR::Key_Tab },
+ { '\n', OVR::Key_Return },
+ { NSPauseFunctionKey, OVR::Key_Pause },
+ { 27, OVR::Key_Escape },
+ { 127, OVR::Key_Backspace },
+ { ' ', OVR::Key_Space },
+ { NSPageUpFunctionKey, OVR::Key_PageUp },
+ { NSPageDownFunctionKey, OVR::Key_PageDown },
+ { NSNextFunctionKey, OVR::Key_PageDown },
+ { NSEndFunctionKey, OVR::Key_End },
+ { NSHomeFunctionKey, OVR::Key_Home },
+ { NSLeftArrowFunctionKey, OVR::Key_Left },
+ { NSUpArrowFunctionKey, OVR::Key_Up },
+ { NSRightArrowFunctionKey, OVR::Key_Right },
+ { NSDownArrowFunctionKey, OVR::Key_Down },
+ { NSInsertFunctionKey, OVR::Key_Insert },
+ { NSDeleteFunctionKey, OVR::Key_Delete },
+ { NSHelpFunctionKey, OVR::Key_Insert },
+};
+
+
+static KeyCode MapToKeyCode(wchar_t vk)
+{
+ unsigned key = Key_None;
+
+ if ((vk >= 'a') && (vk <= 'z'))
+ {
+ key = vk - 'a' + Key_A;
+ }
+ else if ((vk >= ' ') && (vk <= '~'))
+ {
+ key = vk;
+ }
+ else if ((vk >= '0') && (vk <= '9'))
+ {
+ key = vk - '0' + Key_Num0;
+ }
+ else if ((vk >= NSF1FunctionKey) && (vk <= NSF15FunctionKey))
+ {
+ key = vk - NSF1FunctionKey + Key_F1;
+ }
+ else
+ {
+ for (unsigned i = 0; i< (sizeof(KeyMap) / sizeof(KeyMap[1])); i++)
+ {
+ if (vk == KeyMap[i][0])
+ {
+ key = KeyMap[i][1];
+ break;
+ }
+ }
+ }
+
+ return (KeyCode)key;
+}
+
+@implementation OVRView
+
+-(BOOL) acceptsFirstResponder
+{
+ return YES;
+}
+-(BOOL) acceptsFirstMouse:(NSEvent *)ev
+{
+ return YES;
+}
+
++(CGDirectDisplayID) displayFromScreen:(NSScreen *)s
+{
+ NSNumber* didref = (NSNumber*)[[s deviceDescription] objectForKey:@"NSScreenNumber"];
+ CGDirectDisplayID disp = (CGDirectDisplayID)[didref longValue];
+ return disp;
+}
+
+-(void) warpMouseToCenter
+{
+ NSPoint w;
+ w.x = _App->GetWidth()/2.0f;
+ w.y = _App->GetHeight()/2.0f;
+ w = [[self window] convertBaseToScreen:w];
+ CGDirectDisplayID disp = [OVRView displayFromScreen:[[self window] screen]];
+ CGPoint p = {w.x, CGDisplayPixelsHigh(disp)-w.y};
+ CGDisplayMoveCursorToPoint(disp, p);
+}
+
+-(void) keyDown:(NSEvent*)ev
+{
+ NSString* chars = [ev charactersIgnoringModifiers];
+ if ([chars length])
+ {
+ wchar_t ch = [chars characterAtIndex:0];
+ OVR::KeyCode key = MapToKeyCode(ch);
+ _App->OnKey(key, true);
+ }
+}
+-(void) keyUp:(NSEvent*)ev
+{
+ NSString* chars = [ev charactersIgnoringModifiers];
+ if ([chars length])
+ {
+ wchar_t ch = [chars characterAtIndex:0];
+ OVR::KeyCode key = MapToKeyCode(ch);
+ _App->OnKey(key, false);
+
+ }
+}
+
+
+-(void)flagsChanged:(NSEvent *)ev
+{
+ static const OVR::KeyCode ModifierKeys[] = {OVR::Key_None, OVR::Key_Shift, OVR::Key_Control, OVR::Key_Alt, OVR::Key_Meta};
+
+ unsigned long cmods = [ev modifierFlags];
+ if ((cmods & 0xffff0000) != _Modifiers)
+ {
+ uint32_t mods = 0;
+ if (cmods & NSShiftKeyMask)
+ mods |= 0x01;
+ if (cmods & NSControlKeyMask)
+ mods |= 0x02;
+ if (cmods & NSAlternateKeyMask)
+ mods |= 0x04;
+ if (cmods & NSCommandKeyMask)
+ mods |= 0x08;
+
+ for (int i = 1; i <= 4; i++)
+ {
+ unsigned long m = (1 << (16+i));
+ if ((cmods & m) != (_Modifiers & m))
+ {
+ if (cmods & m)
+ _App->OnKey(ModifierKeys[i], true);
+ else
+ _App->OnKey(ModifierKeys[i], false);
+ }
+ }
+ _Modifiers = cmods & 0xffff0000;
+ }
+}
+
+-(void)ProcessMouse:(NSEvent*)ev
+{
+ switch ([ev type])
+ {
+ case NSLeftMouseDragged:
+ case NSRightMouseDragged:
+ case NSOtherMouseDragged:
+ case NSMouseMoved:
+ {
+ int dx = [ev deltaX];
+ int dy = [ev deltaY];
+
+ if (dx != 0 || dy != 0)
+ {
+ _App->OnMouseMove(dx, dy, 0);
+ [self warpMouseToCenter];
+ }
+ }
+ break;
+ case NSLeftMouseDown:
+ case NSRightMouseDown:
+ case NSOtherMouseDown:
+ break;
+ }
+}
+
+-(void) mouseMoved:(NSEvent*)ev
+{
+ [self ProcessMouse:ev];
+}
+-(void) mouseDragged:(NSEvent*)ev
+{
+ [self ProcessMouse:ev];
+}
+
+-(void) mouseDown:(NSEvent*)ev
+{
+ [self warpMouseToCenter];
+ CGAssociateMouseAndMouseCursorPosition(false);
+ [NSCursor hide];
+}
+
+//-(void)
+
+-(id) initWithFrame:(NSRect)frameRect
+{
+ NSOpenGLPixelFormatAttribute attr[] =
+ {NSOpenGLPFAWindow, NSOpenGLPFADoubleBuffer, NSOpenGLPFADepthSize, 24, nil};
+
+ NSOpenGLPixelFormat *pf = [[[NSOpenGLPixelFormat alloc] initWithAttributes:attr] autorelease];
+
+ self = [super initWithFrame:frameRect pixelFormat:pf];
+ GLint swap = 0;
+ [[self openGLContext] setValues:&swap forParameter:NSOpenGLCPSwapInterval];
+ //[self setWantsBestResolutionOpenGLSurface:YES];
+ return self;
+}
+
+-(BOOL)windowShouldClose:(id)sender
+{
+ _App->Exit();
+ return 1;
+}
+
+@end
+
+// GL OSX-specific logic
+namespace OSX {
+
+ RenderDevice::RenderDevice(const RendererParams& p, void* osview, void* context)
+ : GL::RenderDevice(p), Context(context)
+ {
+ OVRView* view = (OVRView*)osview;
+ NSRect bounds = [view bounds];
+ WindowWidth = bounds.size.width;
+ WindowHeight= bounds.size.height;
+
+ }
+
+ RenderDevice* RenderDevice::CreateDevice(const RendererParams& rp, void* osview)
+ {
+ OVRView* view = (OVRView*)osview;
+ NSOpenGLContext *context = [view openGLContext];
+ if (!context)
+ return NULL;
+
+ [context makeCurrentContext];
+ [[view window] makeKeyAndOrderFront:nil];
+
+ return new OSX::RenderDevice(rp, osview, context);
+ }
+
+ void RenderDevice::Present()
+ {
+ NSOpenGLContext *context = (NSOpenGLContext*)Context;
+ [context flushBuffer];
+ }
+
+ void RenderDevice::Shutdown()
+ {
+ Context = NULL;
+ }
+
+ bool RenderDevice::SetFullscreen(DisplayMode fullscreen)
+ {
+ Params.Fullscreen = fullscreen;
+ return 1;
+ }
+
+}
+
+
+int main(int argc, char *argv[])
+{
+ NSApplication* nsapp = [OVRApp sharedApplication];
+ [nsapp run];
+ return 0;
+}
+
diff --git a/Samples/OculusRoomTiny/OculusRoomModel.cpp b/Samples/OculusRoomTiny/OculusRoomModel.cpp
new file mode 100644
index 0000000..74e60cf
--- /dev/null
+++ b/Samples/OculusRoomTiny/OculusRoomModel.cpp
@@ -0,0 +1,260 @@
+/************************************************************************************
+
+Filename : OculusRoomModel.cpp
+Content : Creates a simple room scene from hard-coded geometry
+Created : October 4, 2012
+
+Copyright : Copyright 2012-2013 Oculus, Inc. All Rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+*************************************************************************************/
+
+#include "RenderTiny_Device.h"
+
+using namespace OVR;
+using namespace OVR::RenderTiny;
+
+
+//-------------------------------------------------------------------------------------
+// ***** Room Model
+
+// This model is hard-coded out of axis-aligned solid-colored slabs.
+// Room unit dimensions are in meters. Player starts in the middle.
+//
+
+enum BuiltinTexture
+{
+ Tex_None,
+ Tex_Checker,
+ Tex_Block,
+ Tex_Panel,
+ Tex_Count
+};
+
+struct Slab
+{
+ float x1, y1, z1;
+ float x2, y2, z2;
+ Color c;
+};
+
+struct SlabModel
+{
+ int Count;
+ Slab* pSlabs;
+ BuiltinTexture tex;
+};
+
+Slab FloorSlabs[] =
+{
+ // Floor
+ { -10.0f, -0.1f, -20.0f, 10.0f, 0.0f, 20.1f, Color(128,128,128) }
+};
+
+SlabModel Floor = {sizeof(FloorSlabs)/sizeof(Slab), FloorSlabs, Tex_Checker};
+
+Slab CeilingSlabs[] =
+{
+ { -10.0f, 4.0f, -20.0f, 10.0f, 4.1f, 20.1f, Color(128,128,128) }
+};
+
+SlabModel Ceiling = {sizeof(FloorSlabs)/sizeof(Slab), CeilingSlabs, Tex_Panel};
+
+Slab RoomSlabs[] =
+{
+ // Left Wall
+ { -10.1f, 0.0f, -20.0f, -10.0f, 4.0f, 20.0f, Color(128,128,128) },
+ // Back Wall
+ { -10.0f, -0.1f, -20.1f, 10.0f, 4.0f, -20.0f, Color(128,128,128) },
+
+ // Right Wall
+ { 10.0f, -0.1f, -20.0f, 10.1f, 4.0f, 20.0f, Color(128,128,128) },
+};
+
+SlabModel Room = {sizeof(RoomSlabs)/sizeof(Slab), RoomSlabs, Tex_Block};
+
+Slab FixtureSlabs[] =
+{
+ // Right side shelf
+ { 9.5f, 0.75f, 3.0f, 10.1f, 2.5f, 3.1f, Color(128,128,128) }, // Verticals
+ { 9.5f, 0.95f, 3.7f, 10.1f, 2.75f, 3.8f, Color(128,128,128) },
+ { 9.5f, 1.20f, 2.5f, 10.1f, 1.30f, 3.8f, Color(128,128,128) }, // Horizontals
+ { 9.5f, 2.00f, 3.0f, 10.1f, 2.10f, 4.2f, Color(128,128,128) },
+
+ // Right railing
+ { 5.0f, 1.1f, 20.0f, 10.0f, 1.2f, 20.1f, Color(128,128,128) },
+ // Bars
+ { 9.0f, 1.1f, 20.0f, 9.1f, 0.0f, 20.1f, Color(128,128,128) },
+ { 8.0f, 1.1f, 20.0f, 8.1f, 0.0f, 20.1f, Color(128,128,128) },
+ { 7.0f, 1.1f, 20.0f, 7.1f, 0.0f, 20.1f, Color(128,128,128) },
+ { 6.0f, 1.1f, 20.0f, 6.1f, 0.0f, 20.1f, Color(128,128,128) },
+ { 5.0f, 1.1f, 20.0f, 5.1f, 0.0f, 20.1f, Color(128,128,128) },
+
+ // Left railing
+ { -10.0f, 1.1f, 20.0f, -5.0f, 1.2f, 20.1f, Color(128,128,128) },
+ // Bars
+ { -9.0f, 1.1f, 20.0f, -9.1f, 0.0f, 20.1f, Color(128,128,128) },
+ { -8.0f, 1.1f, 20.0f, -8.1f, 0.0f, 20.1f, Color(128,128,128) },
+ { -7.0f, 1.1f, 20.0f, -7.1f, 0.0f, 20.1f, Color(128,128,128) },
+ { -6.0f, 1.1f, 20.0f, -6.1f, 0.0f, 20.1f, Color(128,128,128) },
+ { -5.0f, 1.1f, 20.0f, -5.1f, 0.0f, 20.1f, Color(128,128,128) },
+
+ // Bottom Floor 2
+ { -15.0f, -6.1f, 18.0f, 15.0f, -6.0f, 30.0f, Color(128,128,128) },
+};
+
+SlabModel Fixtures = {sizeof(FixtureSlabs)/sizeof(Slab), FixtureSlabs};
+
+Slab FurnitureSlabs[] =
+{
+ // Table
+ { -1.8f, 0.7f, 1.0f, 0.0f, 0.8f, 0.0f, Color(128,128,88) },
+ { -1.8f, 0.7f, 0.0f, -1.8f+0.1f, 0.0f, 0.0f+0.1f, Color(128,128,88) }, // Leg 1
+ { -1.8f, 0.7f, 1.0f, -1.8f+0.1f, 0.0f, 1.0f-0.1f, Color(128,128,88) }, // Leg 2
+ { 0.0f, 0.7f, 1.0f, 0.0f-0.1f, 0.0f, 1.0f-0.1f, Color(128,128,88) }, // Leg 2
+ { 0.0f, 0.7f, 0.0f, 0.0f-0.1f, 0.0f, 0.0f+0.1f, Color(128,128,88) }, // Leg 2
+
+ // Chair
+ { -1.4f, 0.5f, -1.1f, -0.8f, 0.55f, -0.5f, Color(88,88,128) }, // Set
+ { -1.4f, 1.0f, -1.1f, -1.4f+0.06f, 0.0f, -1.1f+0.06f, Color(88,88,128) }, // Leg 1
+ { -1.4f, 0.5f, -0.5f, -1.4f+0.06f, 0.0f, -0.5f-0.06f, Color(88,88,128) }, // Leg 2
+ { -0.8f, 0.5f, -0.5f, -0.8f-0.06f, 0.0f, -0.5f-0.06f, Color(88,88,128) }, // Leg 2
+ { -0.8f, 1.0f, -1.1f, -0.8f-0.06f, 0.0f, -1.1f+0.06f, Color(88,88,128) }, // Leg 2
+ { -1.4f, 0.97f,-1.05f,-0.8f, 0.92f, -1.10f, Color(88,88,128) }, // Back high bar
+};
+
+SlabModel Furniture = {sizeof(FurnitureSlabs)/sizeof(Slab), FurnitureSlabs};
+
+Slab PostsSlabs[] =
+{
+ // Posts
+ { 0, 0.0f, 0.0f, 0.1f, 1.3f, 0.1f, Color(128,128,128) },
+ { 0, 0.0f, 0.4f, 0.1f, 1.3f, 0.5f, Color(128,128,128) },
+ { 0, 0.0f, 0.8f, 0.1f, 1.3f, 0.9f, Color(128,128,128) },
+ { 0, 0.0f, 1.2f, 0.1f, 1.3f, 1.3f, Color(128,128,128) },
+ { 0, 0.0f, 1.6f, 0.1f, 1.3f, 1.7f, Color(128,128,128) },
+ { 0, 0.0f, 2.0f, 0.1f, 1.3f, 2.1f, Color(128,128,128) },
+ { 0, 0.0f, 2.4f, 0.1f, 1.3f, 2.5f, Color(128,128,128) },
+ { 0, 0.0f, 2.8f, 0.1f, 1.3f, 2.9f, Color(128,128,128) },
+ { 0, 0.0f, 3.2f, 0.1f, 1.3f, 3.3f, Color(128,128,128) },
+ { 0, 0.0f, 3.6f, 0.1f, 1.3f, 3.7f, Color(128,128,128) },
+};
+
+SlabModel Posts = {sizeof(PostsSlabs)/sizeof(Slab), PostsSlabs};
+
+
+// Temporary helper class used to initialize fills used by model.
+class FillCollection
+{
+public:
+ Ptr<ShaderFill> LitSolid;
+ Ptr<ShaderFill> LitTextures[4];
+
+ FillCollection(RenderDevice* render);
+
+};
+
+FillCollection::FillCollection(RenderDevice* render)
+{
+ Ptr<Texture> builtinTextures[Tex_Count];
+
+ // Create floor checkerboard texture.
+ {
+ Color checker[256*256];
+ for (int j = 0; j < 256; j++)
+ for (int i = 0; i < 256; i++)
+ checker[j*256+i] = (((i/4 >> 5) ^ (j/4 >> 5)) & 1) ?
+ Color(180,180,180,255) : Color(80,80,80,255);
+ builtinTextures[Tex_Checker] = *render->CreateTexture(Texture_RGBA|Texture_GenMipmaps, 256, 256, checker);
+ builtinTextures[Tex_Checker]->SetSampleMode(Sample_Anisotropic|Sample_Repeat);
+ }
+
+ // Ceiling panel texture.
+ {
+ Color panel[256*256];
+ for (int j = 0; j < 256; j++)
+ for (int i = 0; i < 256; i++)
+ panel[j*256+i] = (i/4 == 0 || j/4 == 0) ?
+ Color(80,80,80,255) : Color(180,180,180,255);
+ builtinTextures[Tex_Panel] = *render->CreateTexture(Texture_RGBA|Texture_GenMipmaps, 256, 256, panel);
+ builtinTextures[Tex_Panel]->SetSampleMode(Sample_Anisotropic|Sample_Repeat);
+ }
+
+ // Wall brick textures.
+ {
+ Color block[256*256];
+ for (int j = 0; j < 256; j++)
+ for (int i = 0; i < 256; i++)
+ block[j*256+i] = (((j/4 & 15) == 0) || (((i/4 & 15) == 0) && ((((i/4 & 31) == 0) ^ ((j/4 >> 4) & 1)) == 0))) ?
+ Color(60,60,60,255) : Color(180,180,180,255);
+ builtinTextures[Tex_Block] = *render->CreateTexture(Texture_RGBA|Texture_GenMipmaps, 256, 256, block);
+ builtinTextures[Tex_Block]->SetSampleMode(Sample_Anisotropic|Sample_Repeat);
+ }
+
+ LitSolid = *new ShaderFill(*render->CreateShaderSet());
+ LitSolid->GetShaders()->SetShader(render->LoadBuiltinShader(Shader_Vertex, VShader_MVP));
+ LitSolid->GetShaders()->SetShader(render->LoadBuiltinShader(Shader_Fragment, FShader_LitGouraud));
+
+ for (int i = 1; i < Tex_Count; i++)
+ {
+ LitTextures[i] = *new ShaderFill(*render->CreateShaderSet());
+ LitTextures[i]->GetShaders()->SetShader(render->LoadBuiltinShader(Shader_Vertex, VShader_MVP));
+ LitTextures[i]->GetShaders()->SetShader(render->LoadBuiltinShader(Shader_Fragment, FShader_LitTexture));
+ LitTextures[i]->SetTexture(0, builtinTextures[i]);
+ }
+
+}
+
+
+
+// Helper function to create a model out of Slab arrays.
+Model* CreateModel(Vector3f pos, SlabModel* sm, const FillCollection& fills)
+{
+ Model* m = new Model(Prim_Triangles);
+ m->SetPosition(pos);
+
+ for(int i=0; i< sm->Count; i++)
+ {
+ Slab &s = sm->pSlabs[i];
+ m->AddSolidColorBox(s.x1, s.y1, s.z1, s.x2, s.y2, s.z2, s.c);
+ }
+
+ if (sm->tex > 0)
+ m->Fill = fills.LitTextures[sm->tex];
+ else
+ m->Fill = fills.LitSolid;
+ return m;
+}
+
+
+// Adds sample models and lights to the argument scene.
+void PopulateRoomScene(Scene* scene, RenderDevice* render)
+{
+ FillCollection fills(render);
+
+ scene->World.Add(Ptr<Model>(*CreateModel(Vector3f(0,0,0), &Room, fills)));
+ scene->World.Add(Ptr<Model>(*CreateModel(Vector3f(0,0,0), &Floor, fills)));
+ scene->World.Add(Ptr<Model>(*CreateModel(Vector3f(0,0,0), &Ceiling, fills)));
+ scene->World.Add(Ptr<Model>(*CreateModel(Vector3f(0,0,0), &Fixtures, fills)));
+ scene->World.Add(Ptr<Model>(*CreateModel(Vector3f(0,0,0), &Furniture, fills)));
+ scene->World.Add(Ptr<Model>(*CreateModel(Vector3f(0,0,4), &Furniture, fills)));
+ scene->World.Add(Ptr<Model>(*CreateModel(Vector3f(-3,0,3), &Posts, fills)));
+
+
+ scene->SetAmbient(Vector4f(0.65f,0.65f,0.65f,1));
+ scene->AddLight(Vector3f(-2,4,-2), Vector4f(8,8,8,1));
+ scene->AddLight(Vector3f(3,4,-3), Vector4f(2,1,1,1));
+ scene->AddLight(Vector3f(-4,3,25), Vector4f(3,6,3,1));
+}
+
diff --git a/Samples/OculusRoomTiny/OculusRoomTiny.rc b/Samples/OculusRoomTiny/OculusRoomTiny.rc
new file mode 100644
index 0000000..49a6a5a
--- /dev/null
+++ b/Samples/OculusRoomTiny/OculusRoomTiny.rc
Binary files differ
diff --git a/Samples/OculusRoomTiny/OculusRoomTiny_Msvc2010.vcxproj b/Samples/OculusRoomTiny/OculusRoomTiny_Msvc2010.vcxproj
new file mode 100644
index 0000000..c031772
--- /dev/null
+++ b/Samples/OculusRoomTiny/OculusRoomTiny_Msvc2010.vcxproj
@@ -0,0 +1,187 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{80523489-2881-4F64-8C3B-FAF88B60ABCD}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>OculusRoomTiny</RootNamespace>
+ <ProjectName>OculusRoomTiny</ProjectName>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ <IntDir>$(Configuration)\Obj\</IntDir>
+ <OutDir>$(ProjectDir)$(Configuration)\</OutDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>true</LinkIncremental>
+ <IntDir>$(Configuration)\Obj\</IntDir>
+ <OutDir>$(ProjectDir)$(Configuration)\</OutDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <IntDir>$(Configuration)\Obj\</IntDir>
+ <OutDir>$(ProjectDir)$(Configuration)\</OutDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <IntDir>$(Configuration)\Obj\</IntDir>
+ <OutDir>$(ProjectDir)$(Configuration)\</OutDir>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level4</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>OVR_BUILD_DEBUG;WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>../../LibOVR/Include;../../LibOVR/Src;../../3rdParty/glext;$(DXSDK_DIR)/Include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <DebugInformationFormat>OldStyle</DebugInformationFormat>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <MinimalRebuild>false</MinimalRebuild>
+ </ClCompile>
+ <Link>
+ <SubSystem>Windows</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <AdditionalDependencies>libovrd.lib;dxgi.lib;d3d10.lib;d3d11.lib;d3dcompiler.lib;opengl32.lib;winmm.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <AdditionalLibraryDirectories>../../LibOVR/Lib/Win32;$(DXSDK_DIR)/Lib/x86;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level4</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>OVR_BUILD_DEBUG;WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>../../LibOVR/Include;../../LibOVR/Src;../../3rdParty/glext;$(DXSDK_DIR)/Include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <DebugInformationFormat>OldStyle</DebugInformationFormat>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <MinimalRebuild>false</MinimalRebuild>
+ </ClCompile>
+ <Link>
+ <SubSystem>Windows</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <AdditionalDependencies>libovr64d.lib;dxgi.lib;d3d10.lib;d3d11.lib;d3dcompiler.lib;opengl32.lib;winmm.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <AdditionalLibraryDirectories>../../LibOVR/Lib/x64;$(DXSDK_DIR)/Lib/x64;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level4</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>../../LibOVR/Include;../../LibOVR/Src;../../3rdParty/glext;$(DXSDK_DIR)/Include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <DebugInformationFormat>OldStyle</DebugInformationFormat>
+ </ClCompile>
+ <Link>
+ <SubSystem>Windows</SubSystem>
+ <GenerateDebugInformation>false</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ <AdditionalLibraryDirectories>../../LibOVR/Lib/Win32;$(DXSDK_DIR)/Lib/x86;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <AdditionalDependencies>libovr.lib;dxgi.lib;d3d10.lib;d3d11.lib;d3dcompiler.lib;opengl32.lib;winmm.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <WarningLevel>Level4</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>../../LibOVR/Include;../../LibOVR/Src;../../3rdParty/glext;$(DXSDK_DIR)/Include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <DebugInformationFormat>OldStyle</DebugInformationFormat>
+ </ClCompile>
+ <Link>
+ <SubSystem>Windows</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ <AdditionalLibraryDirectories>../../LibOVR/Lib/x64;$(DXSDK_DIR)/Lib/x64;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <AdditionalDependencies>libovr64.lib;dxgi.lib;d3d10.lib;d3d11.lib;d3dcompiler.lib;opengl32.lib;winmm.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="OculusRoomModel.cpp" />
+ <ClCompile Include="RenderTiny_D3D1X_Device.cpp" />
+ <ClCompile Include="RenderTiny_Device.cpp" />
+ <ClCompile Include="Win32_OculusRoomTiny.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="RenderTiny_D3D1X_Device.h" />
+ <ClInclude Include="RenderTiny_Device.h" />
+ <ClInclude Include="Win32_OculusRoomTiny.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="OculusRoomTiny.rc" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project> \ No newline at end of file
diff --git a/Samples/OculusRoomTiny/OculusRoomTiny_Msvc2010.vcxproj.filters b/Samples/OculusRoomTiny/OculusRoomTiny_Msvc2010.vcxproj.filters
new file mode 100644
index 0000000..f7ec303
--- /dev/null
+++ b/Samples/OculusRoomTiny/OculusRoomTiny_Msvc2010.vcxproj.filters
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <ClCompile Include="OculusRoomModel.cpp" />
+ <ClCompile Include="RenderTiny_D3D1X_Device.cpp" />
+ <ClCompile Include="RenderTiny_Device.cpp" />
+ <ClCompile Include="Win32_OculusRoomTiny.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="RenderTiny_Device.h" />
+ <ClInclude Include="RenderTiny_D3D1X_Device.h" />
+ <ClInclude Include="Win32_OculusRoomTiny.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="OculusRoomTiny.rc" />
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/Samples/OculusRoomTiny/RenderTiny_D3D1X_Device.cpp b/Samples/OculusRoomTiny/RenderTiny_D3D1X_Device.cpp
new file mode 100644
index 0000000..64887dd
--- /dev/null
+++ b/Samples/OculusRoomTiny/RenderTiny_D3D1X_Device.cpp
@@ -0,0 +1,1311 @@
+/************************************************************************************
+
+Filename : RenderTiny_D3D1x.cpp
+Content : RenderDevice implementation for D3DX10/11.
+Created : September 10, 2012
+Authors : Andrew Reisse
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+************************************************************************************/
+
+#include "Kernel/OVR_Log.h"
+#include "Kernel/OVR_Std.h"
+
+#include "RenderTiny_D3D1X_Device.h"
+
+#include <d3dcompiler.h>
+
+namespace OVR { namespace RenderTiny { namespace D3D10 {
+
+
+//-------------------------------------------------------------------------------------
+// Vertex format
+static D3D1x_(INPUT_ELEMENT_DESC) ModelVertexDesc[] =
+{
+ {"Position", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, offsetof(Vertex, Pos), D3D1x_(INPUT_PER_VERTEX_DATA), 0},
+ {"Color", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, offsetof(Vertex, C), D3D1x_(INPUT_PER_VERTEX_DATA), 0},
+ {"TexCoord", 0, DXGI_FORMAT_R32G32_FLOAT, 0, offsetof(Vertex, U), D3D1x_(INPUT_PER_VERTEX_DATA), 0},
+ {"Normal", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, offsetof(Vertex, Norm), D3D1x_(INPUT_PER_VERTEX_DATA), 0},
+};
+
+// These shaders are used to render the world, including lit vertex-colored and textured geometry.
+
+// Used for world geometry; has projection matrix.
+static const char* StdVertexShaderSrc =
+ "float4x4 Proj;\n"
+ "float4x4 View;\n"
+ "struct Varyings\n"
+ "{\n"
+ " float4 Position : SV_Position;\n"
+ " float4 Color : COLOR0;\n"
+ " float2 TexCoord : TEXCOORD0;\n"
+ " float3 Normal : NORMAL;\n"
+ " float3 VPos : TEXCOORD4;\n"
+ "};\n"
+ "void main(in float4 Position : POSITION, in float4 Color : COLOR0, in float2 TexCoord : TEXCOORD0,"
+ " in float3 Normal : NORMAL,\n"
+ " out Varyings ov)\n"
+ "{\n"
+ " ov.Position = mul(Proj, mul(View, Position));\n"
+ " ov.Normal = mul(View, Normal);\n"
+ " ov.VPos = mul(View, Position);\n"
+ " ov.TexCoord = TexCoord;\n"
+ " ov.Color = Color;\n"
+ "}\n";
+
+// Used for text/clearing; no projection.
+static const char* DirectVertexShaderSrc =
+ "float4x4 View : register(c4);\n"
+ "void main(in float4 Position : POSITION, in float4 Color : COLOR0,\n"
+ " in float2 TexCoord : TEXCOORD0, in float3 Normal : NORMAL,\n"
+ " out float4 oPosition : SV_Position, out float4 oColor : COLOR,\n"
+ " out float2 oTexCoord : TEXCOORD0,"
+ " out float3 oNormal : NORMAL)\n"
+ "{\n"
+ " oPosition = mul(View, Position);\n"
+ " oTexCoord = TexCoord;\n"
+ " oColor = Color;\n"
+ " oNormal = mul(View, Normal);\n"
+ "}\n";
+
+static const char* SolidPixelShaderSrc =
+ "float4 Color;\n"
+ "struct Varyings\n"
+ "{\n"
+ " float4 Position : SV_Position;\n"
+ " float4 Color : COLOR0;\n"
+ " float2 TexCoord : TEXCOORD0;\n"
+ "};\n"
+ "float4 main(in Varyings ov) : SV_Target\n"
+ "{\n"
+ " return Color;\n"
+ "}\n";
+
+static const char* GouraudPixelShaderSrc =
+ "struct Varyings\n"
+ "{\n"
+ " float4 Position : SV_Position;\n"
+ " float4 Color : COLOR0;\n"
+ " float2 TexCoord : TEXCOORD0;\n"
+ "};\n"
+ "float4 main(in Varyings ov) : SV_Target\n"
+ "{\n"
+ " return ov.Color;\n"
+ "}\n";
+
+static const char* TexturePixelShaderSrc =
+ "Texture2D Texture : register(t0);\n"
+ "SamplerState Linear : register(s0);\n"
+ "struct Varyings\n"
+ "{\n"
+ " float4 Position : SV_Position;\n"
+ " float4 Color : COLOR0;\n"
+ " float2 TexCoord : TEXCOORD0;\n"
+ "};\n"
+ "float4 main(in Varyings ov) : SV_Target\n"
+ "{\n"
+ " float4 color2 = ov.Color * Texture.Sample(Linear, ov.TexCoord);\n"
+ " if (color2.a <= 0.4)\n"
+ " discard;\n"
+ " return color2;\n"
+ "}\n";
+
+
+#define LIGHTING_COMMON \
+ "cbuffer Lighting : register(b1)\n" \
+ "{\n" \
+ " float3 Ambient;\n" \
+ " float3 LightPos[8];\n" \
+ " float4 LightColor[8];\n" \
+ " float LightCount;\n" \
+ "};\n" \
+ "struct Varyings\n" \
+ "{\n" \
+ " float4 Position : SV_Position;\n" \
+ " float4 Color : COLOR0;\n" \
+ " float2 TexCoord : TEXCOORD0;\n" \
+ " float3 Normal : NORMAL;\n" \
+ " float3 VPos : TEXCOORD4;\n" \
+ "};\n" \
+ "float4 DoLight(Varyings v)\n" \
+ "{\n" \
+ " float3 norm = normalize(v.Normal);\n" \
+ " float3 light = Ambient;\n" \
+ " for (uint i = 0; i < LightCount; i++)\n"\
+ " {\n" \
+ " float3 ltp = (LightPos[i] - v.VPos);\n" \
+ " float ldist = dot(ltp,ltp);\n" \
+ " ltp = normalize(ltp);\n" \
+ " light += saturate(LightColor[i] * v.Color.rgb * dot(norm, ltp) / sqrt(ldist));\n"\
+ " }\n" \
+ " return float4(light, v.Color.a);\n" \
+ "}\n"
+
+static const char* LitSolidPixelShaderSrc =
+ LIGHTING_COMMON
+ "float4 main(in Varyings ov) : SV_Target\n"
+ "{\n"
+ " return DoLight(ov) * ov.Color;\n"
+ "}\n";
+
+static const char* LitTexturePixelShaderSrc =
+ "Texture2D Texture : register(t0);\n"
+ "SamplerState Linear : register(s0);\n"
+ LIGHTING_COMMON
+ "float4 main(in Varyings ov) : SV_Target\n"
+ "{\n"
+ " return DoLight(ov) * Texture.Sample(Linear, ov.TexCoord);\n"
+ "}\n";
+
+
+//-------------------------------------------------------------------------------------
+// ***** Distortion Post-process Shaders
+
+static const char* PostProcessVertexShaderSrc =
+ "float4x4 View : register(c4);\n"
+ "float4x4 Texm : register(c8);\n"
+ "void main(in float4 Position : POSITION, in float4 Color : COLOR0, in float2 TexCoord : TEXCOORD0,\n"
+ " out float4 oPosition : SV_Position, out float4 oColor : COLOR, out float2 oTexCoord : TEXCOORD0)\n"
+ "{\n"
+ " oPosition = mul(View, Position);\n"
+ " oTexCoord = mul(Texm, float4(TexCoord,0,1));\n"
+ " oColor = Color;\n"
+ "}\n";
+
+// Shader with just lens distortion correction.
+static const char* PostProcessPixelShaderSrc =
+ "Texture2D Texture : register(t0);\n"
+ "SamplerState Linear : register(s0);\n"
+ "float2 LensCenter;\n"
+ "float2 ScreenCenter;\n"
+ "float2 Scale;\n"
+ "float2 ScaleIn;\n"
+ "float4 HmdWarpParam;\n"
+ "\n"
+
+ // Scales input texture coordinates for distortion.
+ // ScaleIn maps texture coordinates to Scales to ([-1, 1]), although top/bottom will be
+ // larger due to aspect ratio.
+ "float2 HmdWarp(float2 in01)\n"
+ "{\n"
+ " float2 theta = (in01 - LensCenter) * ScaleIn;\n" // Scales to [-1, 1]
+ " float rSq = theta.x * theta.x + theta.y * theta.y;\n"
+ " float2 theta1 = theta * (HmdWarpParam.x + HmdWarpParam.y * rSq + "
+ " HmdWarpParam.z * rSq * rSq + HmdWarpParam.w * rSq * rSq * rSq);\n"
+ " return LensCenter + Scale * theta1;\n"
+ "}\n"
+
+ "float4 main(in float4 oPosition : SV_Position, in float4 oColor : COLOR,\n"
+ " in float2 oTexCoord : TEXCOORD0) : SV_Target\n"
+ "{\n"
+ " float2 tc = HmdWarp(oTexCoord);\n"
+ " if (any(clamp(tc, ScreenCenter-float2(0.25,0.5), ScreenCenter+float2(0.25, 0.5)) - tc))\n"
+ " return 0;\n"
+ " return Texture.Sample(Linear, tc);\n"
+ "}\n";
+
+// Shader with lens distortion and chromatic aberration correction.
+static const char* PostProcessPixelShaderWithChromAbSrc =
+ "Texture2D Texture : register(t0);\n"
+ "SamplerState Linear : register(s0);\n"
+ "float2 LensCenter;\n"
+ "float2 ScreenCenter;\n"
+ "float2 Scale;\n"
+ "float2 ScaleIn;\n"
+ "float4 HmdWarpParam;\n"
+ "float4 ChromAbParam;\n"
+ "\n"
+
+ // Scales input texture coordinates for distortion.
+ // ScaleIn maps texture coordinates to Scales to ([-1, 1]), although top/bottom will be
+ // larger due to aspect ratio.
+ "float4 main(in float4 oPosition : SV_Position, in float4 oColor : COLOR,\n"
+ " in float2 oTexCoord : TEXCOORD0) : SV_Target\n"
+ "{\n"
+ " float2 theta = (oTexCoord - LensCenter) * ScaleIn;\n" // Scales to [-1, 1]
+ " float rSq= theta.x * theta.x + theta.y * theta.y;\n"
+ " float2 theta1 = theta * (HmdWarpParam.x + HmdWarpParam.y * rSq + "
+ " HmdWarpParam.z * rSq * rSq + HmdWarpParam.w * rSq * rSq * rSq);\n"
+ " \n"
+ " // Detect whether blue texture coordinates are out of range since these will scaled out the furthest.\n"
+ " float2 thetaBlue = theta1 * (ChromAbParam.z + ChromAbParam.w * rSq);\n"
+ " float2 tcBlue = LensCenter + Scale * thetaBlue;\n"
+ " if (any(clamp(tcBlue, ScreenCenter-float2(0.25,0.5), ScreenCenter+float2(0.25, 0.5)) - tcBlue))\n"
+ " return 0;\n"
+ " \n"
+ " // Now do blue texture lookup.\n"
+ " float blue = Texture.Sample(Linear, tcBlue).b;\n"
+ " \n"
+ " // Do green lookup (no scaling).\n"
+ " float2 tcGreen = LensCenter + Scale * theta1;\n"
+ " float green = Texture.Sample(Linear, tcGreen).g;\n"
+ " \n"
+ " // Do red scale and lookup.\n"
+ " float2 thetaRed = theta1 * (ChromAbParam.x + ChromAbParam.y * rSq);\n"
+ " float2 tcRed = LensCenter + Scale * thetaRed;\n"
+ " float red = Texture.Sample(Linear, tcRed).r;\n"
+ " \n"
+ " return float4(red, green, blue, 1);\n"
+ "}\n";
+
+
+static const char* VShaderSrcs[VShader_Count] =
+{
+ DirectVertexShaderSrc,
+ StdVertexShaderSrc,
+ PostProcessVertexShaderSrc
+};
+static const char* FShaderSrcs[FShader_Count] =
+{
+ SolidPixelShaderSrc,
+ GouraudPixelShaderSrc,
+ TexturePixelShaderSrc,
+ PostProcessPixelShaderSrc,
+ PostProcessPixelShaderWithChromAbSrc,
+ LitSolidPixelShaderSrc,
+ LitTexturePixelShaderSrc
+};
+
+
+//-------------------------------------------------------------------------------------
+// ***** Buffer
+
+Buffer::~Buffer()
+{
+}
+
+bool Buffer::Data(int use, const void *buffer, size_t size)
+{
+ if (D3DBuffer && Size >= size)
+ {
+ if (Dynamic)
+ {
+ if (!buffer)
+ return true;
+
+ void* v = Map(0, size, Map_Discard);
+ if (v)
+ {
+ memcpy(v, buffer, size);
+ Unmap(v);
+ return true;
+ }
+ }
+ else
+ {
+ Ren->Context->UpdateSubresource(D3DBuffer, 0, NULL, buffer, 0, 0);
+ return true;
+ }
+ }
+ if (D3DBuffer)
+ {
+ D3DBuffer = NULL;
+ Size = 0;
+ Use = 0;
+ Dynamic = 0;
+ }
+
+ D3D1x_(BUFFER_DESC) desc;
+ memset(&desc, 0, sizeof(desc));
+ if (use & Buffer_ReadOnly)
+ {
+ desc.Usage = D3D1x_(USAGE_IMMUTABLE);
+ desc.CPUAccessFlags = 0;
+ }
+ else
+ {
+ desc.Usage = D3D1x_(USAGE_DYNAMIC);
+ desc.CPUAccessFlags = D3D1x_(CPU_ACCESS_WRITE);
+ Dynamic = 1;
+ }
+
+ switch(use & Buffer_TypeMask)
+ {
+ case Buffer_Vertex: desc.BindFlags = D3D1x_(BIND_VERTEX_BUFFER); break;
+ case Buffer_Index: desc.BindFlags = D3D1x_(BIND_INDEX_BUFFER); break;
+ case Buffer_Uniform:
+ desc.BindFlags = D3D1x_(BIND_CONSTANT_BUFFER);
+ size += ((size + 15) & ~15) - size;
+ break;
+ }
+
+ desc.ByteWidth = (unsigned)size;
+
+ D3D1x_(SUBRESOURCE_DATA) sr;
+ sr.pSysMem = buffer;
+ sr.SysMemPitch = 0;
+ sr.SysMemSlicePitch = 0;
+
+ HRESULT hr = Ren->Device->CreateBuffer(&desc, buffer ? &sr : NULL, &D3DBuffer.GetRawRef());
+ if (SUCCEEDED(hr))
+ {
+ Use = use;
+ Size = desc.ByteWidth;
+ return 1;
+ }
+ return 0;
+}
+
+void* Buffer::Map(size_t start, size_t size, int flags)
+{
+ OVR_UNUSED(size);
+
+ D3D1x_(MAP) mapFlags = D3D1x_(MAP_WRITE);
+ if (flags & Map_Discard)
+ mapFlags = D3D1x_(MAP_WRITE_DISCARD);
+ if (flags & Map_Unsynchronized)
+ mapFlags = D3D1x_(MAP_WRITE_NO_OVERWRITE);
+
+ void* map = 0;
+ if (SUCCEEDED(D3DBuffer->Map(mapFlags, 0, &map)))
+ return ((char*)map) + start;
+ return NULL;
+}
+
+bool Buffer::Unmap(void *m)
+{
+ OVR_UNUSED(m);
+
+ D3DBuffer->Unmap();
+ return true;
+}
+
+
+//-------------------------------------------------------------------------------------
+// Shaders
+
+template<> bool Shader<RenderTiny::Shader_Vertex, ID3D10VertexShader>::Load(void* shader, size_t size)
+{
+ return SUCCEEDED(Ren->Device->CreateVertexShader(shader, size, &D3DShader));
+}
+template<> bool Shader<RenderTiny::Shader_Pixel, ID3D10PixelShader>::Load(void* shader, size_t size)
+{
+ return SUCCEEDED(Ren->Device->CreatePixelShader(shader, size, &D3DShader));
+}
+
+template<> void Shader<RenderTiny::Shader_Vertex, ID3D10VertexShader>::Set(PrimitiveType) const
+{
+ Ren->Context->VSSetShader(D3DShader);
+}
+template<> void Shader<RenderTiny::Shader_Pixel, ID3D10PixelShader>::Set(PrimitiveType) const
+{
+ Ren->Context->PSSetShader(D3DShader);
+}
+
+template<> void Shader<RenderTiny::Shader_Vertex, ID3D1xVertexShader>::SetUniformBuffer(RenderTiny::Buffer* buffer, int i)
+{
+ Ren->Context->VSSetConstantBuffers(i, 1, &((Buffer*)buffer)->D3DBuffer.GetRawRef());
+}
+template<> void Shader<RenderTiny::Shader_Pixel, ID3D1xPixelShader>::SetUniformBuffer(RenderTiny::Buffer* buffer, int i)
+{
+ Ren->Context->PSSetConstantBuffers(i, 1, &((Buffer*)buffer)->D3DBuffer.GetRawRef());
+}
+
+
+//-------------------------------------------------------------------------------------
+// ***** Shader Base
+
+ShaderBase::ShaderBase(RenderDevice* r, ShaderStage stage)
+ : RenderTiny::Shader(stage), Ren(r), UniformData(0)
+{
+}
+ShaderBase::~ShaderBase()
+{
+ if (UniformData)
+ OVR_FREE(UniformData);
+}
+
+bool ShaderBase::SetUniform(const char* name, int n, const float* v)
+{
+ for(unsigned i = 0; i < UniformInfo.GetSize(); i++)
+ {
+ if (!strcmp(UniformInfo[i].Name.ToCStr(), name))
+ {
+ memcpy(UniformData + UniformInfo[i].Offset, v, n * sizeof(float));
+ return 1;
+ }
+ }
+ return 0;
+}
+
+void ShaderBase::InitUniforms(ID3D10Blob* s)
+{
+ ID3D10ShaderReflection* ref = NULL;
+ D3D10ReflectShader(s->GetBufferPointer(), s->GetBufferSize(), &ref);
+ ID3D10ShaderReflectionConstantBuffer* buf = ref->GetConstantBufferByIndex(0);
+ D3D10_SHADER_BUFFER_DESC bufd;
+ if (FAILED(buf->GetDesc(&bufd)))
+ {
+ UniformsSize = 0;
+ if (UniformData)
+ {
+ OVR_FREE(UniformData);
+ UniformData = 0;
+ }
+ return;
+ }
+
+ for(unsigned i = 0; i < bufd.Variables; i++)
+ {
+ ID3D10ShaderReflectionVariable* var = buf->GetVariableByIndex(i);
+ if (var)
+ {
+ D3D10_SHADER_VARIABLE_DESC vd;
+ if (SUCCEEDED(var->GetDesc(&vd)))
+ {
+ Uniform u;
+ u.Name = vd.Name;
+ u.Offset = vd.StartOffset;
+ u.Size = vd.Size;
+ UniformInfo.PushBack(u);
+ }
+ }
+ }
+
+ UniformsSize = bufd.Size;
+ UniformData = (unsigned char*)OVR_ALLOC(bufd.Size);
+}
+
+void ShaderBase::UpdateBuffer(Buffer* buf)
+{
+ if (UniformsSize)
+ {
+ buf->Data(Buffer_Uniform, UniformData, UniformsSize);
+ }
+}
+
+
+//-------------------------------------------------------------------------------------
+// ***** Texture
+//
+Texture::Texture(RenderDevice* ren, int fmt, int w, int h)
+ : Ren(ren), Tex(NULL), TexSv(NULL), TexRtv(NULL), TexDsv(NULL), Width(w), Height(h)
+{
+ OVR_UNUSED(fmt);
+ Sampler = Ren->GetSamplerState(0);
+}
+
+Texture::~Texture()
+{
+}
+
+void Texture::Set(int slot, RenderTiny::ShaderStage stage) const
+{
+ Ren->SetTexture(stage, slot, this);
+}
+
+void Texture::SetSampleMode(int sm)
+{
+ Sampler = Ren->GetSamplerState(sm);
+}
+
+
+
+//-------------------------------------------------------------------------------------
+// ***** Render Device
+
+RenderDevice::RenderDevice(const RendererParams& p, HWND window)
+{
+ RECT rc;
+ GetClientRect(window, &rc);
+ UINT width = rc.right - rc.left;
+ UINT height = rc.bottom - rc.top;
+ WindowWidth = width;
+ WindowHeight = height;
+ Window = window;
+ Params = p;
+
+ HRESULT hr = CreateDXGIFactory(__uuidof(IDXGIFactory), (void**)(&DXGIFactory.GetRawRef()));
+ if (FAILED(hr))
+ return;
+
+ // Find the adapter & output (monitor) to use for fullscreen, based on the reported name of the HMD's monitor.
+ if (Params.MonitorName.GetLength() > 0)
+ {
+ for(UINT AdapterIndex = 0; ; AdapterIndex++)
+ {
+ HRESULT hr = DXGIFactory->EnumAdapters(AdapterIndex, &Adapter.GetRawRef());
+ if (hr == DXGI_ERROR_NOT_FOUND)
+ break;
+
+ DXGI_ADAPTER_DESC Desc;
+ Adapter->GetDesc(&Desc);
+
+ UpdateMonitorOutputs();
+
+ if (FullscreenOutput)
+ break;
+ }
+
+ if (!FullscreenOutput)
+ Adapter = NULL;
+ }
+
+ if (!Adapter)
+ {
+ DXGIFactory->EnumAdapters(0, &Adapter.GetRawRef());
+ }
+
+ int flags = 0;
+
+ hr = D3D10CreateDevice(Adapter, D3D10_DRIVER_TYPE_HARDWARE, NULL, flags, D3D1x_(SDK_VERSION),
+ &Device.GetRawRef());
+ Context = Device;
+ Context->AddRef();
+
+ if (FAILED(hr))
+ return;
+
+ if (!RecreateSwapChain())
+ return;
+
+ if (Params.Fullscreen)
+ SwapChain->SetFullscreenState(1, FullscreenOutput);
+
+ CurRenderTarget = NULL;
+ for(int i = 0; i < Shader_Count; i++)
+ {
+ UniformBuffers[i] = *CreateBuffer();
+ MaxTextureSet[i] = 0;
+ }
+
+ ID3D10Blob* vsData = CompileShader("vs_4_0", DirectVertexShaderSrc);
+ VertexShaders[VShader_MV] = *new VertexShader(this, vsData);
+ for(int i = 1; i < VShader_Count; i++)
+ {
+ VertexShaders[i] = *new VertexShader(this, CompileShader("vs_4_0", VShaderSrcs[i]));
+ }
+
+ for(int i = 0; i < FShader_Count; i++)
+ {
+ PixelShaders[i] = *new PixelShader(this, CompileShader("ps_4_0", FShaderSrcs[i]));
+ }
+
+ SPInt bufferSize = vsData->GetBufferSize();
+ const void* buffer = vsData->GetBufferPointer();
+ ID3D1xInputLayout** objRef = &ModelVertexIL.GetRawRef();
+
+ HRESULT validate = Device->CreateInputLayout(ModelVertexDesc, sizeof(ModelVertexDesc)/sizeof(D3D1x_(INPUT_ELEMENT_DESC)),
+ buffer, bufferSize, objRef);
+ OVR_UNUSED(validate);
+
+ Ptr<ShaderSet> gouraudShaders = *new ShaderSet();
+ gouraudShaders->SetShader(VertexShaders[VShader_MVP]);
+ gouraudShaders->SetShader(PixelShaders[FShader_Gouraud]);
+ DefaultFill = *new ShaderFill(gouraudShaders);
+
+ D3D1x_(BLEND_DESC) bm;
+ memset(&bm, 0, sizeof(bm));
+ bm.BlendEnable[0] = true;
+ bm.BlendOp = bm.BlendOpAlpha = D3D1x_(BLEND_OP_ADD);
+ bm.SrcBlend = bm.SrcBlendAlpha = D3D1x_(BLEND_SRC_ALPHA);
+ bm.DestBlend = bm.DestBlendAlpha = D3D1x_(BLEND_INV_SRC_ALPHA);
+ bm.RenderTargetWriteMask[0] = D3D1x_(COLOR_WRITE_ENABLE_ALL);
+ Device->CreateBlendState(&bm, &BlendState.GetRawRef());
+
+ D3D1x_(RASTERIZER_DESC) rs;
+ memset(&rs, 0, sizeof(rs));
+ rs.AntialiasedLineEnable = true;
+ rs.CullMode = D3D1x_(CULL_BACK);
+ rs.DepthClipEnable = true;
+ rs.FillMode = D3D1x_(FILL_SOLID);
+ Device->CreateRasterizerState(&rs, &Rasterizer.GetRawRef());
+
+ QuadVertexBuffer = *CreateBuffer();
+ const RenderTiny::Vertex QuadVertices[] =
+ { Vertex(Vector3f(0, 1, 0)), Vertex(Vector3f(1, 1, 0)),
+ Vertex(Vector3f(0, 0, 0)), Vertex(Vector3f(1, 0, 0)) };
+ QuadVertexBuffer->Data(Buffer_Vertex, QuadVertices, sizeof(QuadVertices));
+
+ SetDepthMode(0, 0);
+}
+
+RenderDevice::~RenderDevice()
+{
+ if (SwapChain && Params.Fullscreen)
+ {
+ SwapChain->SetFullscreenState(false, NULL);
+ }
+}
+
+
+// Implement static initializer function to create this class.
+RenderTiny::RenderDevice* RenderDevice::CreateDevice(const RendererParams& rp, void* oswnd)
+{
+ return new RenderDevice(rp, (HWND)oswnd);
+}
+
+
+// Fallback monitor enumeration in case newly plugged in monitor wasn't detected.
+// Added originally for the FactoryTest app.
+// New Outputs don't seem to be detected unless adapter is re-created, but that would also
+// require us to re-initialize D3D10 (recreating objects, etc). This bypasses that for "fake"
+// fullscreen modes.
+BOOL CALLBACK MonitorEnumFunc(HMONITOR hMonitor, HDC, LPRECT, LPARAM dwData)
+{
+ RenderDevice* renderer = (RenderDevice*)dwData;
+
+ MONITORINFOEX monitor;
+ monitor.cbSize = sizeof(monitor);
+
+ if (::GetMonitorInfo(hMonitor, &monitor) && monitor.szDevice[0])
+ {
+ DISPLAY_DEVICE dispDev;
+ memset(&dispDev, 0, sizeof(dispDev));
+ dispDev.cb = sizeof(dispDev);
+
+ if (::EnumDisplayDevices(monitor.szDevice, 0, &dispDev, 0))
+ {
+ if (strstr(String(dispDev.DeviceName).ToCStr(), renderer->GetParams().MonitorName.ToCStr()))
+ {
+ renderer->FSDesktopX = monitor.rcMonitor.left;
+ renderer->FSDesktopY = monitor.rcMonitor.top;
+ return FALSE;
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+
+void RenderDevice::UpdateMonitorOutputs()
+{
+ HRESULT hr;
+
+ bool deviceNameFound = false;
+
+ for(UINT OutputIndex = 0; ; OutputIndex++)
+ {
+ Ptr<IDXGIOutput> Output;
+ hr = Adapter->EnumOutputs(OutputIndex, &Output.GetRawRef());
+ if (hr == DXGI_ERROR_NOT_FOUND)
+ {
+ break;
+ }
+
+ DXGI_OUTPUT_DESC OutDesc;
+ Output->GetDesc(&OutDesc);
+
+ MONITORINFOEX monitor;
+ monitor.cbSize = sizeof(monitor);
+ if (::GetMonitorInfo(OutDesc.Monitor, &monitor) && monitor.szDevice[0])
+ {
+ DISPLAY_DEVICE dispDev;
+ memset(&dispDev, 0, sizeof(dispDev));
+ dispDev.cb = sizeof(dispDev);
+
+ if (::EnumDisplayDevices(monitor.szDevice, 0, &dispDev, 0))
+ {
+ if (strstr(String(dispDev.DeviceName).ToCStr(), Params.MonitorName.ToCStr()))
+ {
+ deviceNameFound = true;
+ FullscreenOutput = Output;
+ FSDesktopX = monitor.rcMonitor.left;
+ FSDesktopY = monitor.rcMonitor.top;
+ break;
+ }
+ }
+ }
+ }
+
+ if (!deviceNameFound && !Params.MonitorName.IsEmpty())
+ {
+ EnumDisplayMonitors(0, 0, MonitorEnumFunc, (LPARAM)this);
+ }
+}
+
+bool RenderDevice::RecreateSwapChain()
+{
+ DXGI_SWAP_CHAIN_DESC scDesc;
+ memset(&scDesc, 0, sizeof(scDesc));
+ scDesc.BufferCount = 1;
+ scDesc.BufferDesc.Width = WindowWidth;
+ scDesc.BufferDesc.Height = WindowHeight;
+ scDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
+ scDesc.BufferDesc.RefreshRate.Numerator = 60;
+ scDesc.BufferDesc.RefreshRate.Denominator = 1;
+ scDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
+ scDesc.OutputWindow = Window;
+ scDesc.SampleDesc.Count = Params.Multisample;
+ scDesc.SampleDesc.Quality = 0;
+ scDesc.Windowed = (Params.Fullscreen != Display_Fullscreen);
+ scDesc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
+
+ if (SwapChain)
+ {
+ SwapChain->SetFullscreenState(FALSE, NULL);
+ SwapChain->Release();
+ SwapChain = NULL;
+ }
+
+ Ptr<IDXGISwapChain> newSC;
+ if (FAILED(DXGIFactory->CreateSwapChain(Device, &scDesc, &newSC.GetRawRef())))
+ return false;
+ SwapChain = newSC;
+
+ BackBuffer = NULL;
+ BackBufferRT = NULL;
+ HRESULT hr = SwapChain->GetBuffer(0, __uuidof(ID3D1xTexture2D), (void**)&BackBuffer.GetRawRef());
+ if (FAILED(hr))
+ return false;
+
+ hr = Device->CreateRenderTargetView(BackBuffer, NULL, &BackBufferRT.GetRawRef());
+ if (FAILED(hr))
+ return false;
+
+ Texture* depthBuffer = GetDepthBuffer(WindowWidth, WindowHeight, Params.Multisample);
+ CurDepthBuffer = depthBuffer;
+ if (CurRenderTarget == NULL)
+ {
+ Context->OMSetRenderTargets(1, &BackBufferRT.GetRawRef(), depthBuffer->TexDsv);
+ }
+ return true;
+}
+
+bool RenderDevice::SetParams(const RendererParams& newParams)
+{
+ String oldMonitor = Params.MonitorName;
+
+ Params = newParams;
+ if (newParams.MonitorName != oldMonitor)
+ {
+ UpdateMonitorOutputs();
+ }
+
+ // Cause this to be recreated with the new multisample mode.
+ pSceneColorTex = NULL;
+ return RecreateSwapChain();
+}
+
+
+bool RenderDevice::SetFullscreen(DisplayMode fullscreen)
+{
+ if (fullscreen == Params.Fullscreen)
+ return true;
+
+ HRESULT hr = SwapChain->SetFullscreenState(fullscreen, fullscreen ? FullscreenOutput : NULL);
+ if (FAILED(hr))
+ {
+ return false;
+ }
+
+ Params.Fullscreen = fullscreen;
+ return true;
+}
+
+void RenderDevice::SetRealViewport(const Viewport& vp)
+{
+ D3DViewport.Width = vp.w;
+ D3DViewport.Height = vp.h;
+ D3DViewport.MinDepth = 0;
+ D3DViewport.MaxDepth = 1;
+ D3DViewport.TopLeftX = vp.x;
+ D3DViewport.TopLeftY = vp.y;
+ Context->RSSetViewports(1, &D3DViewport);
+}
+
+static int GetDepthStateIndex(bool enable, bool write, RenderDevice::CompareFunc func)
+{
+ if (!enable)
+ return 0;
+ return 1 + int(func) * 2 + write;
+}
+
+void RenderDevice::SetDepthMode(bool enable, bool write, CompareFunc func)
+{
+ int index = GetDepthStateIndex(enable, write, func);
+ if (DepthStates[index])
+ {
+ CurDepthState = DepthStates[index];
+ Context->OMSetDepthStencilState(DepthStates[index], 0);
+ return;
+ }
+
+ D3D1x_(DEPTH_STENCIL_DESC) dss;
+ memset(&dss, 0, sizeof(dss));
+ dss.DepthEnable = enable;
+ switch(func)
+ {
+ case Compare_Always: dss.DepthFunc = D3D1x_(COMPARISON_ALWAYS); break;
+ case Compare_Less: dss.DepthFunc = D3D1x_(COMPARISON_LESS); break;
+ case Compare_Greater: dss.DepthFunc = D3D1x_(COMPARISON_GREATER); break;
+ default:
+ assert(0);
+ }
+ dss.DepthWriteMask = write ? D3D1x_(DEPTH_WRITE_MASK_ALL) : D3D1x_(DEPTH_WRITE_MASK_ZERO);
+ Device->CreateDepthStencilState(&dss, &DepthStates[index].GetRawRef());
+ Context->OMSetDepthStencilState(DepthStates[index], 0);
+ CurDepthState = DepthStates[index];
+}
+
+Texture* RenderDevice::GetDepthBuffer(int w, int h, int ms)
+{
+ for(unsigned i = 0; i < DepthBuffers.GetSize(); i++)
+ {
+ if (w == DepthBuffers[i]->Width && h == DepthBuffers[i]->Height &&
+ ms == DepthBuffers[i]->Samples)
+ return DepthBuffers[i];
+ }
+
+ Ptr<Texture> newDepth = *CreateTexture(Texture_Depth | Texture_RenderTarget | ms, w, h, NULL);
+ if (newDepth == NULL)
+ {
+ OVR_DEBUG_LOG(("Failed to get depth buffer."));
+ return NULL;
+ }
+
+ DepthBuffers.PushBack(newDepth);
+ return newDepth.GetPtr();
+}
+
+void RenderDevice::Clear(float r, float g, float b, float a, float depth)
+{
+ const float color[] = {r, g, b, a};
+
+ // Needed for each eye to do its own clear, since ClearRenderTargetView doesn't honor viewport.
+
+ // Save state that is affected by clearing this way
+ ID3D1xDepthStencilState* oldDepthState = CurDepthState;
+ StandardUniformData clearUniforms;
+
+ SetDepthMode(true, true, Compare_Always);
+
+ Context->IASetInputLayout(ModelVertexIL);
+ Context->GSSetShader(NULL);
+
+ ID3D1xShaderResourceView* sv[8] = {0, 0, 0, 0, 0, 0, 0, 0};
+ if (MaxTextureSet[Shader_Fragment])
+ {
+ Context->PSSetShaderResources(0, MaxTextureSet[Shader_Fragment], sv);
+ }
+
+ ID3D1xBuffer* vertexBuffer = QuadVertexBuffer->GetBuffer();
+ UINT vertexStride = sizeof(Vertex);
+ UINT vertexOffset = 0;
+ Context->IASetVertexBuffers(0, 1, &vertexBuffer, &vertexStride, &vertexOffset);
+
+ clearUniforms.View = Matrix4f(2, 0, 0, 0,
+ 0, 2, 0, 0,
+ 0, 0, 0, 0,
+ -1, -1, depth, 1);
+ UniformBuffers[Shader_Vertex]->Data(Buffer_Uniform, &clearUniforms, sizeof(clearUniforms));
+
+ ID3D1xBuffer* vertexConstants = UniformBuffers[Shader_Vertex]->GetBuffer();
+ Context->VSSetConstantBuffers(0, 1, &vertexConstants);
+ Context->IASetPrimitiveTopology(D3D1x_(PRIMITIVE_TOPOLOGY_TRIANGLESTRIP));
+ VertexShaders[VShader_MV]->Set(Prim_TriangleStrip);
+ PixelShaders[FShader_Solid]->Set(Prim_TriangleStrip);
+
+ UniformBuffers[Shader_Pixel]->Data(Buffer_Uniform, color, sizeof(color));
+ PixelShaders[FShader_Solid]->SetUniformBuffer(UniformBuffers[Shader_Pixel]);
+
+ // Clear Viewport
+ Context->OMSetBlendState(NULL, NULL, 0xffffffff);
+ Context->Draw(4, 0);
+
+ // reset
+ CurDepthState = oldDepthState;
+ Context->OMSetDepthStencilState(CurDepthState, 0);
+}
+
+// Buffers
+
+Buffer* RenderDevice::CreateBuffer()
+{
+ return new Buffer(this);
+}
+
+
+ID3D10Blob* RenderDevice::CompileShader(const char* profile, const char* src, const char* mainName)
+{
+ ID3D10Blob* shader;
+ ID3D10Blob* errors;
+ HRESULT hr = D3DCompile(src, strlen(src), NULL, NULL, NULL, mainName, profile,
+ 0, 0, &shader, &errors);
+ if (FAILED(hr))
+ {
+ OVR_DEBUG_LOG(("Compiling D3D shader for %s failed\n%s\n\n%s",
+ profile, src, errors->GetBufferPointer()));
+ OutputDebugStringA((char*)errors->GetBufferPointer());
+ return NULL;
+ }
+ if (errors)
+ {
+ errors->Release();
+ }
+ return shader;
+}
+
+void RenderDevice::SetCommonUniformBuffer(int i, RenderTiny::Buffer* buffer)
+{
+ CommonUniforms[i] = (Buffer*)buffer;
+
+ Context->PSSetConstantBuffers(1, 1, &CommonUniforms[1]->D3DBuffer.GetRawRef());
+ Context->VSSetConstantBuffers(1, 1, &CommonUniforms[1]->D3DBuffer.GetRawRef());
+}
+
+RenderTiny::Shader *RenderDevice::LoadBuiltinShader(ShaderStage stage, int shader)
+{
+ switch(stage)
+ {
+ case Shader_Vertex:
+ return VertexShaders[shader];
+ case Shader_Pixel:
+ return PixelShaders[shader];
+ default:
+ return NULL;
+ }
+}
+
+
+ID3D1xSamplerState* RenderDevice::GetSamplerState(int sm)
+{
+ if (SamplerStates[sm])
+ return SamplerStates[sm];
+
+ D3D1x_(SAMPLER_DESC) ss;
+ memset(&ss, 0, sizeof(ss));
+ if (sm & Sample_Clamp)
+ ss.AddressU = ss.AddressV = ss.AddressW = D3D1x_(TEXTURE_ADDRESS_CLAMP);
+ else if (sm & Sample_ClampBorder)
+ ss.AddressU = ss.AddressV = ss.AddressW = D3D1x_(TEXTURE_ADDRESS_BORDER);
+ else
+ ss.AddressU = ss.AddressV = ss.AddressW = D3D1x_(TEXTURE_ADDRESS_WRAP);
+
+ if (sm & Sample_Nearest)
+ {
+ ss.Filter = D3D1x_(FILTER_MIN_MAG_MIP_POINT);
+ }
+ else if (sm & Sample_Anisotropic)
+ {
+ ss.Filter = D3D1x_(FILTER_ANISOTROPIC);
+ ss.MaxAnisotropy = 8;
+ }
+ else
+ {
+ ss.Filter = D3D1x_(FILTER_MIN_MAG_MIP_LINEAR);
+ }
+ ss.MaxLOD = 15;
+ Device->CreateSamplerState(&ss, &SamplerStates[sm].GetRawRef());
+ return SamplerStates[sm];
+}
+
+
+void RenderDevice::SetTexture(RenderTiny::ShaderStage stage, int slot, const Texture* t)
+{
+ if (MaxTextureSet[stage] <= slot)
+ MaxTextureSet[stage] = slot + 1;
+
+ ID3D1xShaderResourceView* sv = t ? t->TexSv : NULL;
+ switch(stage)
+ {
+ case Shader_Fragment:
+ Context->PSSetShaderResources(slot, 1, &sv);
+ if (t)
+ {
+ Context->PSSetSamplers(slot, 1, &t->Sampler.GetRawRef());
+ }
+ break;
+
+ case Shader_Vertex:
+ Context->VSSetShaderResources(slot, 1, &sv);
+ break;
+ }
+}
+
+Texture* RenderDevice::CreateTexture(int format, int width, int height, const void* data, int mipcount)
+{
+ OVR_UNUSED(mipcount);
+
+ DXGI_FORMAT d3dformat;
+ int bpp;
+ switch(format & Texture_TypeMask)
+ {
+ case Texture_RGBA:
+ bpp = 4;
+ d3dformat = DXGI_FORMAT_R8G8B8A8_UNORM;
+ break;
+ case Texture_Depth:
+ bpp = 0;
+ d3dformat = DXGI_FORMAT_D32_FLOAT;
+ break;
+ default:
+ return NULL;
+ }
+
+ int samples = (format & Texture_SamplesMask);
+ if (samples < 1)
+ {
+ samples = 1;
+ }
+
+ Texture* NewTex = new Texture(this, format, width, height);
+ NewTex->Samples = samples;
+
+ D3D1x_(TEXTURE2D_DESC) dsDesc;
+ dsDesc.Width = width;
+ dsDesc.Height = height;
+ dsDesc.MipLevels = (format == (Texture_RGBA | Texture_GenMipmaps) && data) ? GetNumMipLevels(width, height) : 1;
+ dsDesc.ArraySize = 1;
+ dsDesc.Format = d3dformat;
+ dsDesc.SampleDesc.Count = samples;
+ dsDesc.SampleDesc.Quality = 0;
+ dsDesc.Usage = D3D1x_(USAGE_DEFAULT);
+ dsDesc.BindFlags = D3D1x_(BIND_SHADER_RESOURCE);
+ dsDesc.CPUAccessFlags = 0;
+ dsDesc.MiscFlags = 0;
+
+ if (format & Texture_RenderTarget)
+ {
+ if ((format & Texture_TypeMask) == Texture_Depth)
+ { // We don't use depth textures, and creating them in d3d10 requires different options.
+ dsDesc.BindFlags = D3D1x_(BIND_DEPTH_STENCIL);
+ }
+ else
+ {
+ dsDesc.BindFlags |= D3D1x_(BIND_RENDER_TARGET);
+ }
+ }
+
+ HRESULT hr = Device->CreateTexture2D(&dsDesc, NULL, &NewTex->Tex.GetRawRef());
+ if (FAILED(hr))
+ {
+ OVR_DEBUG_LOG_TEXT(("Failed to create 2D D3D texture."));
+ NewTex->Release();
+ return NULL;
+ }
+ if (dsDesc.BindFlags & D3D1x_(BIND_SHADER_RESOURCE))
+ {
+ Device->CreateShaderResourceView(NewTex->Tex, NULL, &NewTex->TexSv.GetRawRef());
+ }
+
+ if (data)
+ {
+ Context->UpdateSubresource(NewTex->Tex, 0, NULL, data, width * bpp, width * height * bpp);
+ if (format == (Texture_RGBA | Texture_GenMipmaps))
+ {
+ int srcw = width, srch = height;
+ int level = 0;
+ UByte* mipmaps = NULL;
+ do
+ {
+ level++;
+ int mipw = srcw >> 1;
+ if (mipw < 1)
+ {
+ mipw = 1;
+ }
+ int miph = srch >> 1;
+ if (miph < 1)
+ {
+ miph = 1;
+ }
+ if (mipmaps == NULL)
+ {
+ mipmaps = (UByte*)OVR_ALLOC(mipw * miph * 4);
+ }
+ FilterRgba2x2(level == 1 ? (const UByte*)data : mipmaps, srcw, srch, mipmaps);
+ Context->UpdateSubresource(NewTex->Tex, level, NULL, mipmaps, mipw * bpp, miph * bpp);
+ srcw = mipw;
+ srch = miph;
+ }
+ while(srcw > 1 || srch > 1);
+
+ if (mipmaps != NULL)
+ {
+ OVR_FREE(mipmaps);
+ }
+ }
+ }
+
+ if (format & Texture_RenderTarget)
+ {
+ if ((format & Texture_TypeMask) == Texture_Depth)
+ {
+ Device->CreateDepthStencilView(NewTex->Tex, NULL, &NewTex->TexDsv.GetRawRef());
+ }
+ else
+ {
+ Device->CreateRenderTargetView(NewTex->Tex, NULL, &NewTex->TexRtv.GetRawRef());
+ }
+ }
+
+ return NewTex;
+}
+
+
+// Rendering
+
+void RenderDevice::BeginRendering()
+{
+ Context->RSSetState(Rasterizer);
+}
+
+void RenderDevice::SetRenderTarget(RenderTiny::Texture* colorTex,
+ RenderTiny::Texture* depth, RenderTiny::Texture* stencil)
+{
+ OVR_UNUSED(stencil);
+
+ CurRenderTarget = (Texture*)colorTex;
+ if (colorTex == NULL)
+ {
+ Texture* newDepthBuffer = GetDepthBuffer(WindowWidth, WindowHeight, Params.Multisample);
+ if (newDepthBuffer == NULL)
+ {
+ OVR_DEBUG_LOG(("New depth buffer creation failed."));
+ }
+ if (newDepthBuffer != NULL)
+ {
+ CurDepthBuffer = GetDepthBuffer(WindowWidth, WindowHeight, Params.Multisample);
+ Context->OMSetRenderTargets(1, &BackBufferRT.GetRawRef(), CurDepthBuffer->TexDsv);
+ }
+ return;
+ }
+ if (depth == NULL)
+ {
+ depth = GetDepthBuffer(colorTex->GetWidth(), colorTex->GetHeight(), CurRenderTarget->Samples);
+ }
+
+ ID3D1xShaderResourceView* sv[8] = {0, 0, 0, 0, 0, 0, 0, 0};
+ if (MaxTextureSet[Shader_Fragment])
+ {
+ Context->PSSetShaderResources(0, MaxTextureSet[Shader_Fragment], sv);
+ }
+ memset(MaxTextureSet, 0, sizeof(MaxTextureSet));
+
+ CurDepthBuffer = (Texture*)depth;
+ Context->OMSetRenderTargets(1, &((Texture*)colorTex)->TexRtv.GetRawRef(), ((Texture*)depth)->TexDsv);
+}
+
+
+void RenderDevice::SetWorldUniforms(const Matrix4f& proj)
+{
+ StdUniforms.Proj = proj.Transposed();
+ // Shader constant buffers cannot be partially updated.
+}
+
+
+void RenderDevice::Render(const Matrix4f& matrix, Model* model)
+{
+ // Store data in buffers if not already
+ if (!model->VertexBuffer)
+ {
+ Ptr<Buffer> vb = *CreateBuffer();
+ vb->Data(Buffer_Vertex, &model->Vertices[0], model->Vertices.GetSize() * sizeof(Vertex));
+ model->VertexBuffer = vb;
+ }
+ if (!model->IndexBuffer)
+ {
+ Ptr<Buffer> ib = *CreateBuffer();
+ ib->Data(Buffer_Index, &model->Indices[0], model->Indices.GetSize() * 2);
+ model->IndexBuffer = ib;
+ }
+
+ Render(model->Fill ? model->Fill : DefaultFill,
+ model->VertexBuffer, model->IndexBuffer,
+ matrix, 0, (unsigned)model->Indices.GetSize(), model->GetPrimType());
+}
+
+void RenderDevice::Render(const ShaderFill* fill, RenderTiny::Buffer* vertices, RenderTiny::Buffer* indices,
+ const Matrix4f& matrix, int offset, int count, PrimitiveType rprim)
+{
+ Context->IASetInputLayout(ModelVertexIL);
+ if (indices)
+ {
+ Context->IASetIndexBuffer(((Buffer*)indices)->GetBuffer(), DXGI_FORMAT_R16_UINT, 0);
+ }
+
+ ID3D1xBuffer* vertexBuffer = ((Buffer*)vertices)->GetBuffer();
+ UINT vertexStride = sizeof(Vertex);
+ UINT vertexOffset = offset;
+ Context->IASetVertexBuffers(0, 1, &vertexBuffer, &vertexStride, &vertexOffset);
+
+ ShaderSet* shaders = ((ShaderFill*)fill)->GetShaders();
+
+ ShaderBase* vshader = ((ShaderBase*)shaders->GetShader(Shader_Vertex));
+ unsigned char* vertexData = vshader->UniformData;
+ if (vertexData)
+ {
+ StandardUniformData* stdUniforms = (StandardUniformData*) vertexData;
+ stdUniforms->View = matrix.Transposed();
+ stdUniforms->Proj = StdUniforms.Proj;
+ UniformBuffers[Shader_Vertex]->Data(Buffer_Uniform, vertexData, vshader->UniformsSize);
+ vshader->SetUniformBuffer(UniformBuffers[Shader_Vertex]);
+ }
+
+ for(int i = Shader_Vertex + 1; i < Shader_Count; i++)
+ if (shaders->GetShader(i))
+ {
+ ((ShaderBase*)shaders->GetShader(i))->UpdateBuffer(UniformBuffers[i]);
+ ((ShaderBase*)shaders->GetShader(i))->SetUniformBuffer(UniformBuffers[i]);
+ }
+
+ D3D1x_(PRIMITIVE_TOPOLOGY) prim;
+ switch(rprim)
+ {
+ case Prim_Triangles:
+ prim = D3D1x_(PRIMITIVE_TOPOLOGY_TRIANGLELIST);
+ break;
+ case Prim_Lines:
+ prim = D3D1x_(PRIMITIVE_TOPOLOGY_LINELIST);
+ break;
+ case Prim_TriangleStrip:
+ prim = D3D1x_(PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
+ break;
+ default:
+ assert(0);
+ return;
+ }
+ Context->IASetPrimitiveTopology(prim);
+
+ fill->Set(rprim);
+
+ if (indices)
+ {
+ Context->DrawIndexed(count, 0, 0);
+ }
+ else
+ {
+ Context->Draw(count, 0);
+ }
+}
+
+
+void RenderDevice::Present()
+{
+ SwapChain->Present(0, 0);
+}
+
+void RenderDevice::ForceFlushGPU()
+{
+ D3D1x_QUERY_DESC queryDesc = { D3D1x_(QUERY_EVENT), 0 };
+ Ptr<ID3D1xQuery> query;
+ BOOL done = FALSE;
+
+ if (Device->CreateQuery(&queryDesc, &query.GetRawRef()) == S_OK)
+ {
+ // Begin() not used for EVENT query.
+ query->End();
+ // GetData will returns S_OK for both done == TRUE or FALSE.
+ // Exit on failure to avoid infinite loop.
+ do { }
+ while(!done && !FAILED(query->GetData(&done, sizeof(BOOL), 0)));
+ }
+}
+
+}}}
+
diff --git a/Samples/OculusRoomTiny/RenderTiny_D3D1X_Device.h b/Samples/OculusRoomTiny/RenderTiny_D3D1X_Device.h
new file mode 100644
index 0000000..e608498
--- /dev/null
+++ b/Samples/OculusRoomTiny/RenderTiny_D3D1X_Device.h
@@ -0,0 +1,273 @@
+/************************************************************************************
+
+Filename : RenderTiny_D3D1X_Device.h
+Content : RenderDevice implementation header for D3DX10.
+Created : September 10, 2012
+Authors : Andrew Reisse
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+************************************************************************************/
+
+#ifndef INC_RenderTiny_D3D1X_Device_h
+#define INC_RenderTiny_D3D1X_Device_h
+
+#include "Kernel/OVR_String.h"
+#include "Kernel/OVR_Array.h"
+
+#include "RenderTiny_Device.h"
+#include <Windows.h>
+
+#define _OVR_RENDERER_D3D10
+#include <d3d10.h>
+
+namespace OVR { namespace RenderTiny { namespace D3D10 {
+
+class RenderDevice;
+class Buffer;
+
+typedef ID3D10Device ID3D1xDevice;
+typedef ID3D10Device ID3D1xDeviceContext;
+typedef ID3D10RenderTargetView ID3D1xRenderTargetView;
+typedef ID3D10Texture2D ID3D1xTexture2D;
+typedef ID3D10ShaderResourceView ID3D1xShaderResourceView;
+typedef ID3D10DepthStencilView ID3D1xDepthStencilView;
+typedef ID3D10DepthStencilState ID3D1xDepthStencilState;
+typedef ID3D10InputLayout ID3D1xInputLayout;
+typedef ID3D10Buffer ID3D1xBuffer;
+typedef ID3D10VertexShader ID3D1xVertexShader;
+typedef ID3D10PixelShader ID3D1xPixelShader;
+typedef ID3D10GeometryShader ID3D1xGeometryShader;
+typedef ID3D10BlendState ID3D1xBlendState;
+typedef ID3D10RasterizerState ID3D1xRasterizerState;
+typedef ID3D10SamplerState ID3D1xSamplerState;
+typedef ID3D10Query ID3D1xQuery;
+typedef ID3D10Blob ID3D1xBlob;
+typedef D3D10_VIEWPORT D3D1x_VIEWPORT;
+typedef D3D10_QUERY_DESC D3D1x_QUERY_DESC;
+#define D3D1x_(x) D3D10_##x
+#define ID3D1x(x) ID3D10##x
+
+
+
+class ShaderBase : public RenderTiny::Shader
+{
+public:
+ RenderDevice* Ren;
+ unsigned char* UniformData;
+ int UniformsSize;
+
+ struct Uniform
+ {
+ String Name;
+ int Offset, Size;
+ };
+ Array<Uniform> UniformInfo;
+
+ ShaderBase(RenderDevice* r, ShaderStage stage);
+ ~ShaderBase();
+
+ void InitUniforms(ID3D10Blob* s);
+ bool SetUniform(const char* name, int n, const float* v);
+
+ void UpdateBuffer(Buffer* b);
+};
+
+template<RenderTiny::ShaderStage SStage, class D3DShaderType>
+class Shader : public ShaderBase
+{
+public:
+ D3DShaderType* D3DShader;
+
+ Shader(RenderDevice* r, D3DShaderType* s) : ShaderBase(r, SStage), D3DShader(s) {}
+ Shader(RenderDevice* r, ID3D1xBlob* s) : ShaderBase(r, SStage)
+ {
+ Load(s);
+ InitUniforms(s);
+ }
+ ~Shader()
+ {
+ if (D3DShader)
+ D3DShader->Release();
+ }
+ bool Load(ID3D1xBlob* shader)
+ {
+ return Load(shader->GetBufferPointer(), shader->GetBufferSize());
+ }
+
+ // These functions have specializations.
+ bool Load(void* shader, size_t size);
+ void Set(PrimitiveType prim) const;
+ void SetUniformBuffer(RenderTiny::Buffer* buffers, int i = 0);
+};
+
+typedef Shader<RenderTiny::Shader_Vertex, ID3D1xVertexShader> VertexShader;
+typedef Shader<RenderTiny::Shader_Fragment, ID3D1xPixelShader> PixelShader;
+
+
+class Buffer : public RenderTiny::Buffer
+{
+public:
+ RenderDevice* Ren;
+ Ptr<ID3D1xBuffer> D3DBuffer;
+ size_t Size;
+ int Use;
+ bool Dynamic;
+
+public:
+ Buffer(RenderDevice* r) : Ren(r), Size(0), Use(0) {}
+ ~Buffer();
+
+ ID3D1xBuffer* GetBuffer()
+ {
+ return D3DBuffer;
+ }
+
+ virtual size_t GetSize()
+ {
+ return Size;
+ }
+ virtual void* Map(size_t start, size_t size, int flags = 0);
+ virtual bool Unmap(void *m);
+ virtual bool Data(int use, const void* buffer, size_t size);
+};
+
+class Texture : public RenderTiny::Texture
+{
+public:
+ RenderDevice* Ren;
+ Ptr<ID3D1xTexture2D> Tex;
+ Ptr<ID3D1xShaderResourceView> TexSv;
+ Ptr<ID3D1xRenderTargetView> TexRtv;
+ Ptr<ID3D1xDepthStencilView> TexDsv;
+ mutable Ptr<ID3D1xSamplerState> Sampler;
+ int Width, Height;
+ int Samples;
+
+ Texture(RenderDevice* r, int fmt, int w, int h);
+ ~Texture();
+
+ virtual int GetWidth() const
+ {
+ return Width;
+ }
+ virtual int GetHeight() const
+ {
+ return Height;
+ }
+ virtual int GetSamples() const
+ {
+ return Samples;
+ }
+
+ virtual void SetSampleMode(int sm);
+
+ virtual void Set(int slot, RenderTiny::ShaderStage stage = RenderTiny::Shader_Fragment) const;
+};
+
+class RenderDevice : public RenderTiny::RenderDevice
+{
+public:
+ Ptr<IDXGIFactory> DXGIFactory;
+ HWND Window;
+
+ Ptr<ID3D1xDevice> Device;
+ Ptr<ID3D1xDeviceContext> Context;
+ Ptr<IDXGISwapChain> SwapChain;
+ Ptr<IDXGIAdapter> Adapter;
+ Ptr<IDXGIOutput> FullscreenOutput;
+ int FSDesktopX, FSDesktopY;
+
+ Ptr<ID3D1xTexture2D> BackBuffer;
+ Ptr<ID3D1xRenderTargetView> BackBufferRT;
+ Ptr<Texture> CurRenderTarget;
+ Ptr<Texture> CurDepthBuffer;
+ Ptr<ID3D1xRasterizerState> Rasterizer;
+ Ptr<ID3D1xBlendState> BlendState;
+ D3D1x_VIEWPORT D3DViewport;
+
+ Ptr<ID3D1xDepthStencilState> DepthStates[1 + 2 * Compare_Count];
+ Ptr<ID3D1xDepthStencilState> CurDepthState;
+ Ptr<ID3D1xInputLayout> ModelVertexIL;
+
+ Ptr<ID3D1xSamplerState> SamplerStates[Sample_Count];
+
+ struct StandardUniformData
+ {
+ Matrix4f Proj;
+ Matrix4f View;
+ } StdUniforms;
+ Ptr<Buffer> UniformBuffers[Shader_Count];
+ int MaxTextureSet[Shader_Count];
+
+ Ptr<VertexShader> VertexShaders[VShader_Count];
+ Ptr<PixelShader> PixelShaders[FShader_Count];
+ Ptr<Buffer> CommonUniforms[8];
+ Ptr<ShaderFill> DefaultFill;
+
+ Ptr<Buffer> QuadVertexBuffer;
+
+ Array<Ptr<Texture> > DepthBuffers;
+
+public:
+ RenderDevice(const RendererParams& p, HWND window);
+ ~RenderDevice();
+
+ // Implement static initializer function to create this class.
+ static RenderTiny::RenderDevice* CreateDevice(const RendererParams& rp, void* oswnd);
+
+ void UpdateMonitorOutputs();
+
+ virtual void SetRealViewport(const Viewport& vp);
+ virtual bool SetParams(const RendererParams& newParams);
+
+ virtual void Present();
+ virtual void ForceFlushGPU();
+
+ virtual bool SetFullscreen(DisplayMode fullscreen);
+
+ virtual void Clear(float r = 0, float g = 0, float b = 0, float a = 1, float depth = 1);
+
+ virtual Buffer* CreateBuffer();
+ virtual Texture* CreateTexture(int format, int width, int height, const void* data, int mipcount=1);
+
+ Texture* GetDepthBuffer(int w, int h, int ms);
+
+ virtual void BeginRendering();
+ virtual void SetRenderTarget(RenderTiny::Texture* color,
+ RenderTiny::Texture* depth = NULL, RenderTiny::Texture* stencil = NULL);
+ virtual void SetDepthMode(bool enable, bool write, CompareFunc func = Compare_Less);
+ virtual void SetWorldUniforms(const Matrix4f& proj);
+ virtual void SetCommonUniformBuffer(int i, RenderTiny::Buffer* buffer);
+
+ virtual void Render(const Matrix4f& matrix, Model* model);
+ virtual void Render(const ShaderFill* fill, RenderTiny::Buffer* vertices, RenderTiny::Buffer* indices,
+ const Matrix4f& matrix, int offset, int count, PrimitiveType prim = Prim_Triangles);
+
+ virtual ShaderFill *CreateSimpleFill() { return DefaultFill; }
+
+ virtual RenderTiny::Shader *LoadBuiltinShader(ShaderStage stage, int shader);
+
+ bool RecreateSwapChain();
+ virtual ID3D10Blob* CompileShader(const char* profile, const char* src, const char* mainName = "main");
+
+ ID3D1xSamplerState* GetSamplerState(int sm);
+
+ void SetTexture(RenderTiny::ShaderStage stage, int slot, const Texture* t);
+};
+
+}}} // Render::D3D10
+
+#endif
diff --git a/Samples/OculusRoomTiny/RenderTiny_Device.cpp b/Samples/OculusRoomTiny/RenderTiny_Device.cpp
new file mode 100644
index 0000000..1d6cdde
--- /dev/null
+++ b/Samples/OculusRoomTiny/RenderTiny_Device.cpp
@@ -0,0 +1,442 @@
+/************************************************************************************
+
+Filename : RenderTiny_Device.cpp
+Content : Platform renderer for simple scene graph - implementation
+Created : September 6, 2012
+Authors : Andrew Reisse
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+************************************************************************************/
+
+#include "RenderTiny_Device.h"
+
+#include "Kernel/OVR_Log.h"
+
+namespace OVR { namespace RenderTiny {
+
+void Model::Render(const Matrix4f& ltw, RenderDevice* ren)
+{
+ if (Visible)
+ {
+ Matrix4f m = ltw * GetMatrix();
+ ren->Render(m, this);
+ }
+}
+
+void Container::Render(const Matrix4f& ltw, RenderDevice* ren)
+{
+ Matrix4f m = ltw * GetMatrix();
+ for(unsigned i = 0; i < Nodes.GetSize(); i++)
+ {
+ Nodes[i]->Render(m, ren);
+ }
+}
+
+void Scene::Render(RenderDevice* ren, const Matrix4f& view)
+{
+ Lighting.Update(view, LightPos);
+
+ ren->SetLighting(&Lighting);
+
+ World.Render(view, ren);
+}
+
+
+
+UInt16 CubeIndices[] =
+{
+ 0, 1, 3,
+ 3, 1, 2,
+
+ 5, 4, 6,
+ 6, 4, 7,
+
+ 8, 9, 11,
+ 11, 9, 10,
+
+ 13, 12, 14,
+ 14, 12, 15,
+
+ 16, 17, 19,
+ 19, 17, 18,
+
+ 21, 20, 22,
+ 22, 20, 23
+};
+
+
+void Model::AddSolidColorBox(float x1, float y1, float z1,
+ float x2, float y2, float z2,
+ Color c)
+{
+ float t;
+
+ if(x1 > x2)
+ {
+ t = x1;
+ x1 = x2;
+ x2 = t;
+ }
+ if(y1 > y2)
+ {
+ t = y1;
+ y1 = y2;
+ y2 = t;
+ }
+ if(z1 > z2)
+ {
+ t = z1;
+ z1 = z2;
+ z2 = t;
+ }
+
+ // Cube vertices and their normals.
+ Vector3f CubeVertices[][3] =
+ {
+ Vector3f(x1, y2, z1), Vector3f(z1, x1), Vector3f(0.0f, 1.0f, 0.0f),
+ Vector3f(x2, y2, z1), Vector3f(z1, x2), Vector3f(0.0f, 1.0f, 0.0f),
+ Vector3f(x2, y2, z2), Vector3f(z2, x2), Vector3f(0.0f, 1.0f, 0.0f),
+ Vector3f(x1, y2, z2), Vector3f(z2, x1), Vector3f(0.0f, 1.0f, 0.0f),
+
+ Vector3f(x1, y1, z1), Vector3f(z1, x1), Vector3f(0.0f, -1.0f, 0.0f),
+ Vector3f(x2, y1, z1), Vector3f(z1, x2), Vector3f(0.0f, -1.0f, 0.0f),
+ Vector3f(x2, y1, z2), Vector3f(z2, x2), Vector3f(0.0f, -1.0f, 0.0f),
+ Vector3f(x1, y1, z2), Vector3f(z2, x1), Vector3f(0.0f, -1.0f, 0.0f),
+
+ Vector3f(x1, y1, z2), Vector3f(z2, y1), Vector3f(-1.0f, 0.0f, 0.0f),
+ Vector3f(x1, y1, z1), Vector3f(z1, y1), Vector3f(-1.0f, 0.0f, 0.0f),
+ Vector3f(x1, y2, z1), Vector3f(z1, y2), Vector3f(-1.0f, 0.0f, 0.0f),
+ Vector3f(x1, y2, z2), Vector3f(z2, y2), Vector3f(-1.0f, 0.0f, 0.0f),
+
+ Vector3f(x2, y1, z2), Vector3f(z2, y1), Vector3f(1.0f, 0.0f, 0.0f),
+ Vector3f(x2, y1, z1), Vector3f(z1, y1), Vector3f(1.0f, 0.0f, 0.0f),
+ Vector3f(x2, y2, z1), Vector3f(z1, y2), Vector3f(1.0f, 0.0f, 0.0f),
+ Vector3f(x2, y2, z2), Vector3f(z2, y2), Vector3f(1.0f, 0.0f, 0.0f),
+
+ Vector3f(x1, y1, z1), Vector3f(x1, y1), Vector3f(0.0f, 0.0f, -1.0f),
+ Vector3f(x2, y1, z1), Vector3f(x2, y1), Vector3f(0.0f, 0.0f, -1.0f),
+ Vector3f(x2, y2, z1), Vector3f(x2, y2), Vector3f(0.0f, 0.0f, -1.0f),
+ Vector3f(x1, y2, z1), Vector3f(x1, y2), Vector3f(0.0f, 0.0f, -1.0f),
+
+ Vector3f(x1, y1, z2), Vector3f(x1, y1), Vector3f(0.0f, 0.0f, 1.0f),
+ Vector3f(x2, y1, z2), Vector3f(x2, y1), Vector3f(0.0f, 0.0f, 1.0f),
+ Vector3f(x2, y2, z2), Vector3f(x2, y2), Vector3f(0.0f, 0.0f, 1.0f),
+ Vector3f(x1, y2, z2), Vector3f(x1, y2), Vector3f(0.0f, 0.0f, 1.0f)
+ };
+
+
+ UInt16 startIndex = GetNextVertexIndex();
+
+ enum
+ {
+ CubeVertexCount = sizeof(CubeVertices) / sizeof(CubeVertices[0]),
+ CubeIndexCount = sizeof(CubeIndices) / sizeof(CubeIndices[0])
+ };
+
+ for(int v = 0; v < CubeVertexCount; v++)
+ {
+ AddVertex(Vertex(CubeVertices[v][0], c, CubeVertices[v][1].x, CubeVertices[v][1].y, CubeVertices[v][2]));
+ }
+
+ // Renumber indices
+ for(int i = 0; i < CubeIndexCount / 3; i++)
+ {
+ AddTriangle(CubeIndices[i * 3] + startIndex,
+ CubeIndices[i * 3 + 1] + startIndex,
+ CubeIndices[i * 3 + 2] + startIndex);
+ }
+}
+
+
+//-------------------------------------------------------------------------------------
+
+
+void ShaderFill::Set(PrimitiveType prim) const
+{
+ Shaders->Set(prim);
+ for(int i = 0; i < 8; i++)
+ {
+ if(Textures[i])
+ {
+ Textures[i]->Set(i);
+ }
+ }
+}
+
+
+
+//-------------------------------------------------------------------------------------
+// ***** Rendering
+
+
+RenderDevice::RenderDevice()
+ : CurPostProcess(PostProcess_None),
+ SceneColorTexW(0), SceneColorTexH(0),
+ SceneRenderScale(1),
+ Distortion(1.0f, 0.18f, 0.115f),
+ PostProcessShaderActive(PostProcessShader_DistortionAndChromAb)
+{
+ PostProcessShaderRequested = PostProcessShaderActive;
+}
+
+ShaderFill* RenderDevice::CreateTextureFill(RenderTiny::Texture* t)
+{
+ ShaderSet* shaders = CreateShaderSet();
+ shaders->SetShader(LoadBuiltinShader(Shader_Vertex, VShader_MVP));
+ shaders->SetShader(LoadBuiltinShader(Shader_Fragment, FShader_Texture));
+ ShaderFill* f = new ShaderFill(*shaders);
+ f->SetTexture(0, t);
+ return f;
+}
+
+void RenderDevice::SetLighting(const LightingParams* lt)
+{
+ if (!LightingBuffer)
+ LightingBuffer = *CreateBuffer();
+
+ LightingBuffer->Data(Buffer_Uniform, lt, sizeof(LightingParams));
+ SetCommonUniformBuffer(1, LightingBuffer);
+}
+
+
+
+void RenderDevice::SetSceneRenderScale(float ss)
+{
+ SceneRenderScale = ss;
+ pSceneColorTex = NULL;
+}
+
+void RenderDevice::SetViewport(const Viewport& vp)
+{
+ VP = vp;
+
+ if (CurPostProcess == PostProcess_Distortion)
+ {
+ Viewport svp = vp;
+ svp.w = (int)ceil(SceneRenderScale * vp.w);
+ svp.h = (int)ceil(SceneRenderScale * vp.h);
+ svp.x = (int)ceil(SceneRenderScale * vp.x);
+ svp.y = (int)ceil(SceneRenderScale * vp.y);
+ SetRealViewport(svp);
+ }
+ else
+ {
+ SetRealViewport(vp);
+ }
+}
+
+
+bool RenderDevice::initPostProcessSupport(PostProcessType pptype)
+{
+ if (pptype != PostProcess_Distortion)
+ return true;
+
+
+ if (PostProcessShaderRequested != PostProcessShaderActive)
+ {
+ pPostProcessShader.Clear();
+ PostProcessShaderActive = PostProcessShaderRequested;
+ }
+
+ if (!pPostProcessShader)
+ {
+ Shader *vs = LoadBuiltinShader(Shader_Vertex, VShader_PostProcess);
+
+ Shader *ppfs = NULL;
+ if (PostProcessShaderActive == PostProcessShader_Distortion)
+ {
+ ppfs = LoadBuiltinShader(Shader_Fragment, FShader_PostProcess);
+ }
+ else if (PostProcessShaderActive == PostProcessShader_DistortionAndChromAb)
+ {
+ ppfs = LoadBuiltinShader(Shader_Fragment, FShader_PostProcessWithChromAb);
+ }
+ else
+ OVR_ASSERT(false);
+
+ pPostProcessShader = *CreateShaderSet();
+ pPostProcessShader->SetShader(vs);
+ pPostProcessShader->SetShader(ppfs);
+ }
+
+
+ int texw = (int)ceil(SceneRenderScale * WindowWidth),
+ texh = (int)ceil(SceneRenderScale * WindowHeight);
+
+ // If pSceneColorTex is already created and is of correct size, we are done.
+ // It's important to check width/height in case window size changed.
+ if (pSceneColorTex && (texw == SceneColorTexW) && (texh == SceneColorTexH))
+ {
+ return true;
+ }
+
+ pSceneColorTex = *CreateTexture(Texture_RGBA | Texture_RenderTarget | Params.Multisample,
+ texw, texh, NULL);
+ if (!pSceneColorTex)
+ {
+ return false;
+ }
+ SceneColorTexW = texw;
+ SceneColorTexH = texh;
+ pSceneColorTex->SetSampleMode(Sample_ClampBorder | Sample_Linear);
+
+
+ if (!pFullScreenVertexBuffer)
+ {
+ pFullScreenVertexBuffer = *CreateBuffer();
+ const RenderTiny::Vertex QuadVertices[] =
+ {
+ Vertex(Vector3f(0, 1, 0), Color(1, 1, 1, 1), 0, 0),
+ Vertex(Vector3f(1, 1, 0), Color(1, 1, 1, 1), 1, 0),
+ Vertex(Vector3f(0, 0, 0), Color(1, 1, 1, 1), 0, 1),
+ Vertex(Vector3f(1, 0, 0), Color(1, 1, 1, 1), 1, 1)
+ };
+ pFullScreenVertexBuffer->Data(Buffer_Vertex, QuadVertices, sizeof(QuadVertices));
+ }
+ return true;
+}
+
+void RenderDevice::SetProjection(const Matrix4f& proj)
+{
+ Proj = proj;
+ SetWorldUniforms(proj);
+}
+
+void RenderDevice::BeginScene(PostProcessType pptype)
+{
+ BeginRendering();
+
+ if ((pptype != PostProcess_None) && initPostProcessSupport(pptype))
+ {
+ CurPostProcess = pptype;
+ }
+ else
+ {
+ CurPostProcess = PostProcess_None;
+ }
+
+ if (CurPostProcess == PostProcess_Distortion)
+ {
+ SetRenderTarget(pSceneColorTex);
+ SetViewport(VP);
+ }
+ else
+ {
+ SetRenderTarget(0);
+ }
+
+ SetWorldUniforms(Proj);
+}
+
+void RenderDevice::FinishScene()
+{
+ if (CurPostProcess == PostProcess_None)
+ return;
+
+ SetRenderTarget(0);
+ SetRealViewport(VP);
+ FinishScene1();
+
+ CurPostProcess = PostProcess_None;
+}
+
+
+
+void RenderDevice::FinishScene1()
+{
+ // Clear with black
+ Clear(0.0f, 0.0f, 0.0f, 1.0f);
+
+ float w = float(VP.w) / float(WindowWidth),
+ h = float(VP.h) / float(WindowHeight),
+ x = float(VP.x) / float(WindowWidth),
+ y = float(VP.y) / float(WindowHeight);
+
+ float as = float(VP.w) / float(VP.h);
+
+ // We are using 1/4 of DistortionCenter offset value here, since it is
+ // relative to [-1,1] range that gets mapped to [0, 0.5].
+ pPostProcessShader->SetUniform2f("LensCenter",
+ x + (w + Distortion.XCenterOffset * 0.5f)*0.5f, y + h*0.5f);
+ pPostProcessShader->SetUniform2f("ScreenCenter", x + w*0.5f, y + h*0.5f);
+
+ // MA: This is more correct but we would need higher-res texture vertically; we should adopt this
+ // once we have asymmetric input texture scale.
+ float scaleFactor = 1.0f / Distortion.Scale;
+
+ pPostProcessShader->SetUniform2f("Scale", (w/2) * scaleFactor, (h/2) * scaleFactor * as);
+ pPostProcessShader->SetUniform2f("ScaleIn", (2/w), (2/h) / as);
+
+ pPostProcessShader->SetUniform4f("HmdWarpParam",
+ Distortion.K[0], Distortion.K[1], Distortion.K[2], Distortion.K[3]);
+
+ if (PostProcessShaderRequested == PostProcessShader_DistortionAndChromAb)
+ {
+ pPostProcessShader->SetUniform4f("ChromAbParam",
+ Distortion.ChromaticAberration[0],
+ Distortion.ChromaticAberration[1],
+ Distortion.ChromaticAberration[2],
+ Distortion.ChromaticAberration[3]);
+ }
+
+ Matrix4f texm(w, 0, 0, x,
+ 0, h, 0, y,
+ 0, 0, 0, 0,
+ 0, 0, 0, 1);
+ pPostProcessShader->SetUniform4x4f("Texm", texm);
+
+ Matrix4f view(2, 0, 0, -1,
+ 0, 2, 0, -1,
+ 0, 0, 0, 0,
+ 0, 0, 0, 1);
+
+ ShaderFill fill(pPostProcessShader);
+ fill.SetTexture(0, pSceneColorTex);
+ Render(&fill, pFullScreenVertexBuffer, NULL, view, 0, 4, Prim_TriangleStrip);
+}
+
+
+int GetNumMipLevels(int w, int h)
+{
+ int n = 1;
+ while(w > 1 || h > 1)
+ {
+ w >>= 1;
+ h >>= 1;
+ n++;
+ }
+ return n;
+}
+
+void FilterRgba2x2(const UByte* src, int w, int h, UByte* dest)
+{
+ for(int j = 0; j < (h & ~1); j += 2)
+ {
+ const UByte* psrc = src + (w * j * 4);
+ UByte* pdest = dest + ((w >> 1) * (j >> 1) * 4);
+
+ for(int i = 0; i < w >> 1; i++, psrc += 8, pdest += 4)
+ {
+ pdest[0] = (((int)psrc[0]) + psrc[4] + psrc[w * 4 + 0] + psrc[w * 4 + 4]) >> 2;
+ pdest[1] = (((int)psrc[1]) + psrc[5] + psrc[w * 4 + 1] + psrc[w * 4 + 5]) >> 2;
+ pdest[2] = (((int)psrc[2]) + psrc[6] + psrc[w * 4 + 2] + psrc[w * 4 + 6]) >> 2;
+ pdest[3] = (((int)psrc[3]) + psrc[7] + psrc[w * 4 + 3] + psrc[w * 4 + 7]) >> 2;
+ }
+ }
+}
+
+
+}}
diff --git a/Samples/OculusRoomTiny/RenderTiny_Device.h b/Samples/OculusRoomTiny/RenderTiny_Device.h
new file mode 100644
index 0000000..74ad7ec
--- /dev/null
+++ b/Samples/OculusRoomTiny/RenderTiny_Device.h
@@ -0,0 +1,724 @@
+/************************************************************************************
+
+Filename : RenderTiny_Device.h
+Content : Minimal possible renderer for RoomTiny sample
+Created : September 6, 2012
+Authors : Andrew Reisse, Michael Antonov
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+************************************************************************************/
+
+#ifndef OVR_RenderTiny_Device_h
+#define OVR_RenderTiny_Device_h
+
+#include "Kernel/OVR_Math.h"
+#include "Kernel/OVR_Array.h"
+#include "Kernel/OVR_RefCount.h"
+#include "Kernel/OVR_String.h"
+#include "Kernel/OVR_File.h"
+#include "Kernel/OVR_Color.h"
+
+#include "Util/Util_Render_Stereo.h"
+
+namespace OVR { namespace RenderTiny {
+
+using namespace OVR::Util::Render;
+
+class RenderDevice;
+
+
+//-----------------------------------------------------------------------------------
+
+// Rendering primitive type used to render Model.
+enum PrimitiveType
+{
+ Prim_Triangles,
+ Prim_Lines,
+ Prim_TriangleStrip,
+ Prim_Unknown,
+ Prim_Count
+};
+
+// Types of shaders taht can be stored together in a ShaderSet.
+enum ShaderStage
+{
+ Shader_Vertex = 0,
+ Shader_Fragment = 2,
+ Shader_Pixel = 2,
+ Shader_Count = 3,
+};
+
+// Built-in shader types; used by LoadBuiltinShader.
+enum BuiltinShaders
+{
+ VShader_MV = 0,
+ VShader_MVP = 1,
+ VShader_PostProcess = 2,
+ VShader_Count = 3,
+
+ FShader_Solid = 0,
+ FShader_Gouraud = 1,
+ FShader_Texture = 2,
+ FShader_PostProcess = 3,
+ FShader_PostProcessWithChromAb = 4,
+ FShader_LitGouraud = 5,
+ FShader_LitTexture = 6,
+ FShader_Count
+};
+
+
+enum MapFlags
+{
+ Map_Discard = 1,
+ Map_Read = 2, // do not use
+ Map_Unsynchronized = 4, // like D3D11_MAP_NO_OVERWRITE
+};
+
+// Buffer types used for uploading geometry & constants.
+enum BufferUsage
+{
+ Buffer_Unknown = 0,
+ Buffer_Vertex = 1,
+ Buffer_Index = 2,
+ Buffer_Uniform = 4,
+ Buffer_TypeMask = 0xff,
+ Buffer_ReadOnly = 0x100, // Buffer must be created with Data().
+};
+
+enum TextureFormat
+{
+ Texture_RGBA = 0x0100,
+ Texture_Depth = 0x8000,
+ Texture_TypeMask = 0xff00,
+ Texture_SamplesMask = 0x00ff,
+ Texture_RenderTarget = 0x10000,
+ Texture_GenMipmaps = 0x20000,
+};
+
+// Texture sampling modes.
+enum SampleMode
+{
+ Sample_Linear = 0,
+ Sample_Nearest = 1,
+ Sample_Anisotropic = 2,
+ Sample_FilterMask = 3,
+
+ Sample_Repeat = 0,
+ Sample_Clamp = 4,
+ Sample_ClampBorder = 8, // If unsupported Clamp is used instead.
+ Sample_AddressMask =12,
+
+ Sample_Count =13,
+};
+
+// A vector with a dummy w component for alignment in uniform buffers (and for float colors).
+// The w component is not used in any calculations.
+struct Vector4f : public Vector3f
+{
+ float w;
+
+ Vector4f() : w(1) {}
+ Vector4f(const Vector3f& v) : Vector3f(v), w(1) {}
+ Vector4f(float r, float g, float b, float a) : Vector3f(r,g,b), w(a) {}
+};
+
+
+// Base class for vertex and pixel shaders. Stored in ShaderSet.
+class Shader : public RefCountBase<Shader>
+{
+ friend class ShaderSet;
+
+protected:
+ ShaderStage Stage;
+
+public:
+ Shader(ShaderStage s) : Stage(s) {}
+ virtual ~Shader() {}
+
+ ShaderStage GetStage() const { return Stage; }
+
+ virtual void Set(PrimitiveType) const { }
+ virtual void SetUniformBuffer(class Buffer* buffers, int i = 0) { OVR_UNUSED2(buffers, i); }
+
+protected:
+ virtual bool SetUniform(const char* name, int n, const float* v) { OVR_UNUSED3(name, n, v); return false; }
+};
+
+
+// A group of shaders, one per stage.
+// A ShaderSet is applied to a RenderDevice for rendering with a given fill.
+class ShaderSet : public RefCountBase<ShaderSet>
+{
+ protected:
+ Ptr<Shader> Shaders[Shader_Count];
+
+public:
+ ShaderSet() { }
+ ~ShaderSet() { }
+
+ virtual void SetShader(Shader *s)
+ {
+ Shaders[s->GetStage()] = s;
+ }
+ virtual void UnsetShader(int stage)
+ {
+ Shaders[stage] = NULL;
+ }
+ Shader* GetShader(int stage) { return Shaders[stage]; }
+
+ virtual void Set(PrimitiveType prim) const
+ {
+ for (int i = 0; i < Shader_Count; i++)
+ if (Shaders[i])
+ Shaders[i]->Set(prim);
+ }
+
+ // Set a uniform (other than the standard matrices). It is undefined whether the
+ // uniforms from one shader occupy the same space as those in other shaders
+ // (unless a buffer is used, then each buffer is independent).
+ virtual bool SetUniform(const char* name, int n, const float* v)
+ {
+ bool result = 0;
+ for (int i = 0; i < Shader_Count; i++)
+ if (Shaders[i])
+ result |= Shaders[i]->SetUniform(name, n, v);
+
+ return result;
+ }
+ bool SetUniform1f(const char* name, float x)
+ {
+ const float v[] = {x};
+ return SetUniform(name, 1, v);
+ }
+ bool SetUniform2f(const char* name, float x, float y)
+ {
+ const float v[] = {x,y};
+ return SetUniform(name, 2, v);
+ }
+ bool SetUniform4f(const char* name, float x, float y, float z, float w = 1)
+ {
+ const float v[] = {x,y,z,w};
+ return SetUniform(name, 4, v);
+ }
+ bool SetUniformv(const char* name, const Vector3f& v)
+ {
+ const float a[] = {v.x,v.y,v.z,1};
+ return SetUniform(name, 4, a);
+ }
+ bool SetUniform4fv(const char* name, int n, const Vector4f* v)
+ {
+ return SetUniform(name, 4*n, &v[0].x);
+ }
+ virtual bool SetUniform4x4f(const char* name, const Matrix4f& m)
+ {
+ Matrix4f mt = m.Transposed();
+ return SetUniform(name, 16, &mt.M[0][0]);
+ }
+};
+
+
+// Fill combines a ShaderSet (vertex, pixel) with textures, if any.
+// Every model has a fill.
+class ShaderFill : public RefCountBase<ShaderFill>
+{
+ Ptr<ShaderSet> Shaders;
+ Ptr<class Texture> Textures[8];
+
+public:
+ ShaderFill(ShaderSet* sh) : Shaders(sh) { }
+ ShaderFill(ShaderSet& sh) : Shaders(sh) { }
+
+ ShaderSet* GetShaders() { return Shaders; }
+
+ virtual void Set(PrimitiveType prim = Prim_Unknown) const;
+ virtual void SetTexture(int i, class Texture* tex) { if (i < 8) Textures[i] = tex; }
+};
+
+
+// Buffer for vertex or index data. Some renderers require separate buffers, so that
+// is recommended. Some renderers cannot have high-performance buffers which are readable,
+// so reading in Map should not be relied on.
+//
+// Constraints on buffers, such as ReadOnly, are not enforced by the API but may result in
+// rendering-system dependent undesirable behavior, such as terrible performance or unreported failure.
+//
+// Use of a buffer inconsistent with usage is also not checked by the API, but it may result in bad
+// performance or even failure.
+//
+// Use the Data() function to set buffer data the first time, if possible (it may be faster).
+
+class Buffer : public RefCountBase<Buffer>
+{
+public:
+ virtual ~Buffer() {}
+
+ virtual size_t GetSize() = 0;
+ virtual void* Map(size_t start, size_t size, int flags = 0) = 0;
+ virtual bool Unmap(void *m) = 0;
+
+ // Allocates a buffer, optionally filling it with data.
+ virtual bool Data(int use, const void* buffer, size_t size) = 0;
+};
+
+class Texture : public RefCountBase<Texture>
+{
+public:
+ virtual ~Texture() {}
+
+ virtual int GetWidth() const = 0;
+ virtual int GetHeight() const = 0;
+ virtual int GetSamples() const { return 1; }
+
+ virtual void SetSampleMode(int sm) = 0;
+ virtual void Set(int slot, ShaderStage stage = Shader_Fragment) const = 0;
+};
+
+
+
+//-----------------------------------------------------------------------------------
+
+// Node is a base class for geometry in a Scene, it contains base position
+// and orientation data.
+// Model and Container both derive from it.
+//
+class Node : public RefCountBase<Node>
+{
+ Vector3f Pos;
+ Quatf Rot;
+
+ mutable Matrix4f Mat;
+ mutable bool MatCurrent;
+
+public:
+ Node() : Pos(Vector3f(0)), MatCurrent(1) { }
+ virtual ~Node() { }
+
+ enum NodeType
+ {
+ Node_NonDisplay,
+ Node_Container,
+ Node_Model
+ };
+ virtual NodeType GetType() const { return Node_NonDisplay; }
+
+ const Vector3f& GetPosition() const { return Pos; }
+ const Quatf& GetOrientation() const { return Rot; }
+ void SetPosition(Vector3f p) { Pos = p; MatCurrent = 0; }
+ void SetOrientation(Quatf q) { Rot = q; MatCurrent = 0; }
+
+ void Move(Vector3f p) { Pos += p; MatCurrent = 0; }
+ void Rotate(Quatf q) { Rot = q * Rot; MatCurrent = 0; }
+
+
+ // For testing only; causes Position an Orientation
+ void SetMatrix(const Matrix4f& m)
+ {
+ MatCurrent = true;
+ Mat = m;
+ }
+
+ const Matrix4f& GetMatrix() const
+ {
+ if (!MatCurrent)
+ {
+ Mat = Rot;
+ Mat = Matrix4f::Translation(Pos) * Mat;
+ MatCurrent = 1;
+ }
+ return Mat;
+ }
+
+ virtual void Render(const Matrix4f& ltw, RenderDevice* ren) { OVR_UNUSED2(ltw, ren); }
+};
+
+
+// Vertex type; same format is used for all shapes for simplicity.
+// Shapes are built by adding vertices to Model.
+struct Vertex
+{
+ Vector3f Pos;
+ Color C;
+ float U, V;
+ Vector3f Norm;
+
+ Vertex (const Vector3f& p, const Color& c = Color(64,0,0,255),
+ float u = 0, float v = 0, Vector3f n = Vector3f(1,0,0))
+ : Pos(p), C(c), U(u), V(v), Norm(n)
+ {}
+ Vertex(float x, float y, float z, const Color& c = Color(64,0,0,255),
+ float u = 0, float v = 0) : Pos(x,y,z), C(c), U(u), V(v)
+ { }
+
+ bool operator==(const Vertex& b) const
+ {
+ return Pos == b.Pos && C == b.C && U == b.U && V == b.V;
+ }
+};
+
+// LightingParams are stored in a uniform buffer, don't change it without fixing all renderers
+// Scene contains a set of LightingParams that is uses for rendering.
+struct LightingParams
+{
+ Vector4f Ambient;
+ Vector4f LightPos[8];
+ Vector4f LightColor[8];
+ float LightCount;
+ int Version;
+
+ LightingParams() : LightCount(0), Version(0) {}
+
+
+ void Update(const Matrix4f& view, const Vector4f* SceneLightPos)
+ {
+ Version++;
+ for (int i = 0; i < LightCount; i++)
+ {
+ LightPos[i] = view.Transform(SceneLightPos[i]);
+ }
+ }
+
+ void Set(ShaderSet* s) const
+ {
+ s->SetUniform4fv("Ambient", 1, &Ambient);
+ s->SetUniform1f("LightCount", LightCount);
+ s->SetUniform4fv("LightPos", (int)LightCount, LightPos);
+ s->SetUniform4fv("LightColor", (int)LightCount, LightColor);
+ }
+
+};
+
+//-----------------------------------------------------------------------------------
+
+// Model is a triangular mesh with a fill that can be added to scene.
+//
+class Model : public Node
+{
+public:
+ Array<Vertex> Vertices;
+ Array<UInt16> Indices;
+ PrimitiveType Type;
+ Ptr<ShaderFill> Fill;
+ bool Visible;
+
+ // Some renderers will create these if they didn't exist before rendering.
+ // Currently they are not updated, so vertex data should not be changed after rendering.
+ Ptr<Buffer> VertexBuffer;
+ Ptr<Buffer> IndexBuffer;
+
+ Model(PrimitiveType t = Prim_Triangles) : Type(t), Fill(NULL), Visible(true) { }
+ ~Model() { }
+
+ PrimitiveType GetPrimType() const { return Type; }
+
+ void SetVisible(bool visible) { Visible = visible; }
+ bool IsVisible() const { return Visible; }
+
+
+ // Node implementation.
+ virtual NodeType GetType() const { return Node_Model; }
+ virtual void Render(const Matrix4f& ltw, RenderDevice* ren);
+
+
+ // Returns the index next added vertex will have.
+ UInt16 GetNextVertexIndex() const
+ {
+ return (UInt16)Vertices.GetSize();
+ }
+
+ UInt16 AddVertex(const Vertex& v)
+ {
+ assert(!VertexBuffer && !IndexBuffer);
+ UInt16 index = (UInt16)Vertices.GetSize();
+ Vertices.PushBack(v);
+ return index;
+ }
+
+ void AddTriangle(UInt16 a, UInt16 b, UInt16 c)
+ {
+ Indices.PushBack(a);
+ Indices.PushBack(b);
+ Indices.PushBack(c);
+ }
+
+ // Uses texture coordinates for uniform world scaling (must use a repeat sampler).
+ void AddSolidColorBox(float x1, float y1, float z1,
+ float x2, float y2, float z2,
+ Color c);
+};
+
+
+// Container stores a collection of rendering nodes (Models or other containers).
+class Container : public Node
+{
+public:
+ Array<Ptr<Node> > Nodes;
+
+ Container() { }
+ ~Container() { }
+
+ virtual NodeType GetType() const { return Node_Container; }
+
+ virtual void Render(const Matrix4f& ltw, RenderDevice* ren);
+
+ void Add(Node *n) { Nodes.PushBack(n); }
+ void Clear() { Nodes.Clear(); }
+};
+
+
+// Scene combines a collection of model
+class Scene
+{
+public:
+ Container World;
+ Vector4f LightPos[8];
+ LightingParams Lighting;
+
+public:
+ void Render(RenderDevice* ren, const Matrix4f& view);
+
+ void SetAmbient(Vector4f color)
+ {
+ Lighting.Ambient = color;
+ }
+
+ void AddLight(Vector3f pos, Vector4f color)
+ {
+ int n = (int)Lighting.LightCount;
+ OVR_ASSERT(n < 8);
+ LightPos[n] = pos;
+ Lighting.LightColor[n] = color;
+ Lighting.LightCount++;
+ }
+
+ void Clear()
+ {
+ World.Clear();
+ Lighting.Ambient = Vector4f(0.0f, 0.0f, 0.0f, 0.0f);
+ Lighting.LightCount = 0;
+ }
+ };
+
+
+//-----------------------------------------------------------------------------------
+
+// Post-processing type to apply to scene after rendering. PostProcess_Distortion
+// applied distortion as described by DistortionConfig.
+enum PostProcessType
+{
+ PostProcess_None,
+ PostProcess_Distortion
+};
+
+enum DisplayMode
+{
+ Display_Window = 0,
+ Display_Fullscreen = 1
+};
+
+
+// Rendering parameters used by RenderDevice::CreateDevice.
+struct RendererParams
+{
+ int Multisample;
+ int Fullscreen;
+
+ // Windows - Monitor name for fullscreen mode.
+ String MonitorName;
+ // MacOS
+ long DisplayId;
+
+ RendererParams(int ms = 1) : Multisample(ms), Fullscreen(0) {}
+
+ bool IsDisplaySet() const
+ {
+ return MonitorName.GetLength() || DisplayId;
+ }
+};
+
+
+
+//-----------------------------------------------------------------------------------
+// ***** RenderDevice
+
+// Rendering device abstraction.
+// Provides platform-independent part of implementation, with platform-specific
+// part being in a separate derived class/file, such as D3D10::RenderDevice.
+//
+class RenderDevice : public RefCountBase<RenderDevice>
+{
+protected:
+ int WindowWidth, WindowHeight;
+ RendererParams Params;
+ Viewport VP;
+
+ Matrix4f Proj;
+ Ptr<Buffer> pTextVertexBuffer;
+
+ // For rendering with lens warping
+ PostProcessType CurPostProcess;
+ Ptr<Texture> pSceneColorTex; // Distortion render target, both eyes.
+ int SceneColorTexW;
+ int SceneColorTexH;
+ Ptr<ShaderSet> pPostProcessShader;
+ Ptr<Buffer> pFullScreenVertexBuffer;
+ float SceneRenderScale;
+ DistortionConfig Distortion;
+
+ // For lighting on platforms with uniform buffers
+ Ptr<Buffer> LightingBuffer;
+
+ void FinishScene1();
+
+public:
+ enum CompareFunc
+ {
+ Compare_Always = 0,
+ Compare_Less = 1,
+ Compare_Greater = 2,
+ Compare_Count
+ };
+ RenderDevice();
+ virtual ~RenderDevice() { Shutdown(); }
+
+ // This static function is implemented in each derived class
+ // to support a specific renderer type.
+ //static RenderDevice* CreateDevice(const RendererParams& rp, void* oswnd);
+
+
+ virtual void Init() {}
+ virtual void Shutdown() {}
+ virtual bool SetParams(const RendererParams&) { return 0; }
+
+ const RendererParams& GetParams() const { return Params; }
+
+
+ // StereoParams apply Viewport, Projection and Distortion simultaneously,
+ // doing full configuration for one eye.
+ void ApplyStereoParams(const StereoEyeParams& params)
+ {
+ SetViewport(params.VP);
+ SetProjection(params.Projection);
+ if (params.pDistortion)
+ SetDistortionConfig(*params.pDistortion, params.Eye);
+ }
+
+ virtual void SetViewport(const Viewport& vp);
+ void SetViewport(int x, int y, int w, int h) { SetViewport(Viewport(x,y,w,h)); }
+
+ // PostProcess distortion
+ void SetSceneRenderScale(float ss);
+
+ void SetDistortionConfig(const DistortionConfig& config, StereoEye eye = StereoEye_Left)
+ {
+ Distortion = config;
+ if (eye == StereoEye_Right)
+ Distortion.XCenterOffset = -Distortion.XCenterOffset;
+ }
+
+ // Set viewport ignoring any adjustments used for the stereo mode.
+ virtual void SetRealViewport(const Viewport& vp) = 0;
+
+ virtual void Clear(float r = 0, float g = 0, float b = 0, float a = 1, float depth = 1) = 0;
+
+ virtual bool IsFullscreen() const { return Params.Fullscreen != Display_Window; }
+ virtual void Present() = 0;
+ // Waits for rendering to complete; important for reducing latency.
+ virtual void ForceFlushGPU() { }
+
+ // Resources
+ virtual Buffer* CreateBuffer() { return NULL; }
+ virtual Texture* CreateTexture(int format, int width, int height, const void* data, int mipcount=1)
+ { OVR_UNUSED5(format,width,height,data, mipcount); return NULL; }
+
+
+ virtual ShaderSet* CreateShaderSet() { return new ShaderSet; }
+ virtual Shader* LoadBuiltinShader(ShaderStage stage, int shader) = 0;
+
+ // Rendering
+
+ // Begin drawing directly to the currently selected render target, no post-processing.
+ virtual void BeginRendering() {}
+ // Begin drawing the primary scene. This will have post-processing applied (if enabled)
+ // during FinishScene.
+ virtual void BeginScene(PostProcessType pp = PostProcess_None);
+ // Postprocess the scene and return to the screen render target.
+ virtual void FinishScene();
+
+ // Texture must have been created with Texture_RenderTarget. Use NULL for the default render target.
+ // NULL depth buffer means use an internal, temporary one.
+ virtual void SetRenderTarget(Texture* color, Texture* depth = NULL, Texture* stencil = NULL)
+ { OVR_UNUSED3(color, depth, stencil); }
+ virtual void SetDepthMode(bool enable, bool write, CompareFunc func = Compare_Less) = 0;
+ virtual void SetProjection(const Matrix4f& proj);
+ virtual void SetWorldUniforms(const Matrix4f& proj) = 0;
+
+ // The data is not copied, it must remain valid until the end of the frame
+ virtual void SetLighting(const LightingParams* light);
+
+ // The index 0 is reserved for non-buffer uniforms, and so cannot be used with this function.
+ virtual void SetCommonUniformBuffer(int i, Buffer* buffer) { OVR_UNUSED2(i, buffer); }
+
+ virtual Matrix4f GetProjection() const { return Proj; }
+
+ // This is a View matrix only, it will be combined with the projection matrix from SetProjection
+ virtual void Render(const Matrix4f& matrix, Model* model) = 0;
+ // offset is in bytes; indices can be null.
+ virtual void Render(const ShaderFill* fill, Buffer* vertices, Buffer* indices,
+ const Matrix4f& matrix, int offset, int count, PrimitiveType prim = Prim_Triangles) = 0;
+
+ virtual ShaderFill *CreateSimpleFill() = 0;
+ ShaderFill * CreateTextureFill(Texture* tex);
+
+
+ // Don't call these directly, use App/Platform instead
+ virtual bool SetFullscreen(DisplayMode fullscreen) { OVR_UNUSED(fullscreen); return false; }
+
+
+ enum PostProcessShader
+ {
+ PostProcessShader_Distortion = 0,
+ PostProcessShader_DistortionAndChromAb = 1,
+ PostProcessShader_Count
+ };
+
+ PostProcessShader GetPostProcessShader()
+ {
+ return PostProcessShaderActive;
+ }
+
+ void SetPostProcessShader(PostProcessShader newShader)
+ {
+ PostProcessShaderRequested = newShader;
+ }
+
+protected:
+ // Stereo & post-processing
+ virtual bool initPostProcessSupport(PostProcessType pptype);
+
+private:
+ PostProcessShader PostProcessShaderRequested;
+ PostProcessShader PostProcessShaderActive;
+};
+
+int GetNumMipLevels(int w, int h);
+
+// Filter an rgba image with a 2x2 box filter, for mipmaps.
+// Image size must be a power of 2.
+void FilterRgba2x2(const UByte* src, int w, int h, UByte* dest);
+
+}} // OVR::RenderTiny
+
+#endif
diff --git a/Samples/OculusRoomTiny/RenderTiny_GL_Device.cpp b/Samples/OculusRoomTiny/RenderTiny_GL_Device.cpp
new file mode 100644
index 0000000..197c62a
--- /dev/null
+++ b/Samples/OculusRoomTiny/RenderTiny_GL_Device.cpp
@@ -0,0 +1,784 @@
+/************************************************************************************
+
+Filename : RenderTiny_GL_Device.cpp
+Content : RenderDevice implementation for OpenGL (tiny version)
+Created : September 10, 2012
+Authors : Andrew Reisse, Artem Bolgar
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+************************************************************************************/
+
+#include "RenderTiny_GL_Device.h"
+#include "Kernel/OVR_Log.h"
+
+namespace OVR { namespace RenderTiny { namespace GL {
+
+
+
+static const char* StdVertexShaderSrc =
+ "uniform mat4 Proj;\n"
+ "uniform mat4 View;\n"
+ "attribute vec4 Position;\n"
+ "attribute vec4 Color;\n"
+ "attribute vec2 TexCoord;\n"
+ "attribute vec3 Normal;\n"
+ "varying vec4 oColor;\n"
+ "varying vec2 oTexCoord;\n"
+ "varying vec3 oNormal;\n"
+ "varying vec3 oVPos;\n"
+ "void main()\n"
+ "{\n"
+ " gl_Position = Proj * (View * Position);\n"
+ " oNormal = vec3(View * vec4(Normal,0));\n"
+ " oVPos = vec3(View * Position);\n"
+ " oTexCoord = TexCoord;\n"
+ " oColor = Color;\n"
+ "}\n";
+
+static const char* DirectVertexShaderSrc =
+ "uniform mat4 View;\n"
+ "attribute vec4 Position;\n"
+ "attribute vec4 Color;\n"
+ "attribute vec2 TexCoord;\n"
+ "attribute vec3 Normal;\n"
+ "varying vec4 oColor;\n"
+ "varying vec2 oTexCoord;\n"
+ "varying vec3 oNormal;\n"
+ "void main()\n"
+ "{\n"
+ " gl_Position = View * Position;\n"
+ " oTexCoord = TexCoord;\n"
+ " oColor = Color;\n"
+ " oNormal = vec3(View * vec4(Normal,0));\n"
+ "}\n";
+
+static const char* SolidFragShaderSrc =
+ "uniform vec4 Color;\n"
+ "void main()\n"
+ "{\n"
+ " gl_FragColor = Color;\n"
+ "}\n";
+
+static const char* GouraudFragShaderSrc =
+ "varying vec4 oColor;\n"
+ "void main()\n"
+ "{\n"
+ " gl_FragColor = oColor;\n"
+ "}\n";
+
+static const char* TextureFragShaderSrc =
+ "uniform sampler2D Texture0;\n"
+ "varying vec4 oColor;\n"
+ "varying vec2 oTexCoord;\n"
+ "void main()\n"
+ "{\n"
+ " gl_FragColor = oColor * texture2D(Texture0, oTexCoord);\n"
+ " if (gl_FragColor.a < 0.4)\n"
+ " discard;\n"
+ "}\n";
+
+#define LIGHTING_COMMON \
+ "uniform vec3 Ambient;\n" \
+ "uniform vec4 LightPos[8];\n" \
+ "uniform vec4 LightColor[8];\n" \
+ "uniform float LightCount;\n" \
+ "varying vec4 oColor;\n" \
+ "varying vec2 oTexCoord;\n" \
+ "varying vec3 oNormal;\n" \
+ "varying vec3 oVPos;\n" \
+ "vec4 DoLight()\n" \
+ "{\n" \
+ " vec3 norm = normalize(oNormal);\n" \
+ " vec3 light = Ambient;\n" \
+ " for (int i = 0; i < int(LightCount); i++)\n" \
+ " {\n" \
+ " vec3 ltp = (LightPos[i].xyz - oVPos);\n" \
+ " float ldist = length(ltp);\n" \
+ " ltp = normalize(ltp);\n" \
+ " light += clamp(LightColor[i].rgb * oColor.rgb * (dot(norm, ltp) / ldist), 0.0,1.0);\n" \
+ " }\n" \
+ " return vec4(light, oColor.a);\n" \
+ "}\n"
+
+static const char* LitSolidFragShaderSrc =
+ LIGHTING_COMMON
+ "void main()\n"
+ "{\n"
+ " gl_FragColor = DoLight() * oColor;\n"
+ "}\n";
+
+static const char* LitTextureFragShaderSrc =
+ "uniform sampler2D Texture0;\n"
+ LIGHTING_COMMON
+ "void main()\n"
+ "{\n"
+ " gl_FragColor = DoLight() * texture2D(Texture0, oTexCoord);\n"
+ "}\n";
+
+static const char* PostProcessVertexShaderSrc =
+ "uniform mat4 View;\n"
+ "uniform mat4 Texm;\n"
+ "attribute vec4 Position;\n"
+ "attribute vec2 TexCoord;\n"
+ "varying vec2 oTexCoord;\n"
+ "void main()\n"
+ "{\n"
+ " gl_Position = View * Position;\n"
+ " oTexCoord = vec2(Texm * vec4(TexCoord,0,1));\n"
+ " oTexCoord.y = 1.0-oTexCoord.y;\n"
+ "}\n";
+
+static const char* PostProcessFragShaderSrc =
+ "uniform vec2 LensCenter;\n"
+ "uniform vec2 ScreenCenter;\n"
+ "uniform vec2 Scale;\n"
+ "uniform vec2 ScaleIn;\n"
+ "uniform vec4 HmdWarpParam;\n"
+ "uniform sampler2D Texture0;\n"
+ "varying vec2 oTexCoord;\n"
+ "\n"
+ "vec2 HmdWarp(vec2 in01)\n"
+ "{\n"
+ " vec2 theta = (in01 - LensCenter) * ScaleIn;\n" // Scales to [-1, 1]
+ " float rSq = theta.x * theta.x + theta.y * theta.y;\n"
+ " vec2 theta1 = theta * (HmdWarpParam.x + HmdWarpParam.y * rSq + "
+ " HmdWarpParam.z * rSq * rSq + HmdWarpParam.w * rSq * rSq * rSq);\n"
+ " return LensCenter + Scale * theta1;\n"
+ "}\n"
+ "void main()\n"
+ "{\n"
+ " vec2 tc = HmdWarp(oTexCoord);\n"
+ " if (!all(equal(clamp(tc, ScreenCenter-vec2(0.25,0.5), ScreenCenter+vec2(0.25,0.5)), tc)))\n"
+ " gl_FragColor = vec4(0);\n"
+ " else\n"
+ " gl_FragColor = texture2D(Texture0, tc);\n"
+ "}\n";
+
+// Shader with lens distortion and chromatic aberration correction.
+static const char* PostProcessFullFragShaderSrc =
+ "uniform vec2 LensCenter;\n"
+ "uniform vec2 ScreenCenter;\n"
+ "uniform vec2 Scale;\n"
+ "uniform vec2 ScaleIn;\n"
+ "uniform vec4 HmdWarpParam;\n"
+ "uniform vec4 ChromAbParam;\n"
+ "uniform sampler2D Texture0;\n"
+ "varying vec2 oTexCoord;\n"
+ "\n"
+ // Scales input texture coordinates for distortion.
+ // ScaleIn maps texture coordinates to Scales to ([-1, 1]), although top/bottom will be
+ // larger due to aspect ratio.
+ "void main()\n"
+ "{\n"
+ " vec2 theta = (oTexCoord - LensCenter) * ScaleIn;\n" // Scales to [-1, 1]
+ " float rSq= theta.x * theta.x + theta.y * theta.y;\n"
+ " vec2 theta1 = theta * (HmdWarpParam.x + HmdWarpParam.y * rSq + "
+ " HmdWarpParam.z * rSq * rSq + HmdWarpParam.w * rSq * rSq * rSq);\n"
+ " \n"
+ " // Detect whether blue texture coordinates are out of range since these will scaled out the furthest.\n"
+ " vec2 thetaBlue = theta1 * (ChromAbParam.z + ChromAbParam.w * rSq);\n"
+ " vec2 tcBlue = LensCenter + Scale * thetaBlue;\n"
+ " if (!all(equal(clamp(tcBlue, ScreenCenter-vec2(0.25,0.5), ScreenCenter+vec2(0.25,0.5)), tcBlue)))\n"
+ " {\n"
+ " gl_FragColor = vec4(0);\n"
+ " return;\n"
+ " }\n"
+ " \n"
+ " // Now do blue texture lookup.\n"
+ " float blue = texture2D(Texture0, tcBlue).b;\n"
+ " \n"
+ " // Do green lookup (no scaling).\n"
+ " vec2 tcGreen = LensCenter + Scale * theta1;\n"
+ " vec4 center = texture2D(Texture0, tcGreen);\n"
+ " \n"
+ " // Do red scale and lookup.\n"
+ " vec2 thetaRed = theta1 * (ChromAbParam.x + ChromAbParam.y * rSq);\n"
+ " vec2 tcRed = LensCenter + Scale * thetaRed;\n"
+ " float red = texture2D(Texture0, tcRed).r;\n"
+ " \n"
+ " gl_FragColor = vec4(red, center.g, blue, 1);\n"
+ "}\n";
+
+static const char* VShaderSrcs[VShader_Count] =
+{
+ DirectVertexShaderSrc,
+ StdVertexShaderSrc,
+ PostProcessVertexShaderSrc
+};
+static const char* FShaderSrcs[FShader_Count] =
+{
+ SolidFragShaderSrc,
+ GouraudFragShaderSrc,
+ TextureFragShaderSrc,
+ PostProcessFragShaderSrc,
+ PostProcessFullFragShaderSrc,
+ LitSolidFragShaderSrc,
+ LitTextureFragShaderSrc
+};
+
+
+
+RenderDevice::RenderDevice(const RendererParams& p)
+{
+ for (int i = 0; i < VShader_Count; i++)
+ VertexShaders[i] = *new Shader(this, Shader_Vertex, VShaderSrcs[i]);
+
+ for (int i = 0; i < FShader_Count; i++)
+ FragShaders[i] = *new Shader(this, Shader_Fragment, FShaderSrcs[i]);
+
+ Ptr<ShaderSet> gouraudShaders = *new ShaderSet();
+ gouraudShaders->SetShader(VertexShaders[VShader_MVP]);
+ gouraudShaders->SetShader(FragShaders[FShader_Gouraud]);
+ DefaultFill = *new ShaderFill(gouraudShaders);
+
+ glGenFramebuffersEXT(1, &CurrentFbo);
+}
+
+Shader *RenderDevice::LoadBuiltinShader(ShaderStage stage, int shader)
+{
+ switch (stage)
+ {
+ case Shader_Vertex: return VertexShaders[shader];
+ case Shader_Fragment: return FragShaders[shader];
+ default:
+ return NULL;
+ }
+}
+
+
+void RenderDevice::BeginRendering()
+{
+ glEnable(GL_DEPTH_TEST);
+ glEnable(GL_CULL_FACE);
+ glFrontFace(GL_CW);
+
+ glLineWidth(3.0f);
+ glEnable(GL_LINE_SMOOTH);
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+}
+
+void RenderDevice::SetDepthMode(bool enable, bool write, CompareFunc func)
+{
+ if (enable)
+ {
+ glEnable(GL_DEPTH_TEST);
+ glDepthMask(write);
+ switch (func)
+ {
+ case Compare_Always: glDepthFunc(GL_ALWAYS); break;
+ case Compare_Less: glDepthFunc(GL_LESS); break;
+ case Compare_Greater: glDepthFunc(GL_GREATER); break;
+ default: assert(0);
+ }
+ }
+ else
+ glDisable(GL_DEPTH_TEST);
+}
+
+void RenderDevice::SetRealViewport(const Viewport& vp)
+{
+ int wh;
+ if (CurRenderTarget)
+ wh = CurRenderTarget->Height;
+ else
+ wh = WindowHeight;
+ glViewport(vp.x, wh-vp.y-vp.h, vp.w, vp.h);
+
+ glEnable(GL_SCISSOR_TEST);
+ glScissor(vp.x, wh-vp.y-vp.h, vp.w, vp.h);
+}
+
+void RenderDevice::Clear(float r, float g, float b, float a, float depth)
+{
+ glClearColor(r,g,b,a);
+ glClearDepth(depth);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
+}
+
+RBuffer* RenderDevice::GetDepthBuffer(int w, int h, int ms)
+{
+ for (unsigned i = 0; i < DepthBuffers.GetSize(); i++)
+ if (w == DepthBuffers[i]->Width && h == DepthBuffers[i]->Height)// && ms == DepthBuffers[i]->Samples)
+ return DepthBuffers[i];
+
+ //Ptr<Texture> newDepth = *CreateTexture(Texture_Depth|Texture_RenderTarget|ms, w, h, NULL);
+ Ptr<RBuffer> newDepth = *new RBuffer(GL_DEPTH24_STENCIL8, w, h); // combined depth stencil
+ DepthBuffers.PushBack(newDepth);
+ return newDepth.GetPtr();
+}
+
+void RenderDevice::SetRenderTarget(RenderTiny::Texture* color, RenderTiny::Texture*, RenderTiny::Texture* stencil)
+{
+ OVR_UNUSED(stencil);
+
+ CurRenderTarget = (Texture*)color;
+ if (color == NULL)
+ {
+ glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
+ return;
+ }
+ //if (depth == NULL)
+ RBuffer* depth = GetDepthBuffer(color->GetWidth(), color->GetHeight(), 0); //CurRenderTarget->Samples);
+
+ glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, CurrentFbo);
+ glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, ((Texture*)color)->TexId, 0);
+ if (depth)
+ //glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, ((Texture*)depth)->TexId, 0);
+ glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, ((RBuffer*)depth)->BufId);
+ else
+ glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, 0);
+
+ GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
+ if (status != GL_FRAMEBUFFER_COMPLETE_EXT)
+ OVR_DEBUG_LOG(("framebuffer not complete: %x", status));
+}
+
+
+void RenderDevice::SetWorldUniforms(const Matrix4f& proj)
+{
+ Proj = proj.Transposed();
+}
+
+void RenderDevice::SetTexture(RenderTiny::ShaderStage, int slot, const Texture* t)
+{
+ glActiveTexture(GL_TEXTURE0 + slot);
+ glBindTexture(GL_TEXTURE_2D, ((Texture*)t)->TexId);
+ glActiveTexture(GL_TEXTURE0);
+}
+
+Buffer* RenderDevice::CreateBuffer()
+{
+ return new Buffer(this);
+}
+
+void RenderDevice::Render(const Matrix4f& matrix, Model* model)
+{
+ // Store data in buffers if not already
+ if (!model->VertexBuffer)
+ {
+ Ptr<RenderTiny::Buffer> vb = *CreateBuffer();
+ vb->Data(Buffer_Vertex, &model->Vertices[0], model->Vertices.GetSize() * sizeof(Vertex));
+ model->VertexBuffer = vb;
+ }
+ if (!model->IndexBuffer)
+ {
+ Ptr<RenderTiny::Buffer> ib = *CreateBuffer();
+ ib->Data(Buffer_Index, &model->Indices[0], model->Indices.GetSize() * 2);
+ model->IndexBuffer = ib;
+ }
+
+ Render(model->Fill ? (const ShaderFill*)model->Fill : (const ShaderFill*)DefaultFill,
+ model->VertexBuffer, model->IndexBuffer,
+ matrix, 0, (int)model->Indices.GetSize(), model->GetPrimType());
+}
+
+void RenderDevice::Render(const ShaderFill* fill, RenderTiny::Buffer* vertices, RenderTiny::Buffer* indices,
+ const Matrix4f& matrix, int offset, int count, PrimitiveType rprim)
+{
+ ShaderSet* shaders = (ShaderSet*) ((ShaderFill*)fill)->GetShaders();
+
+ GLenum prim;
+ switch (rprim)
+ {
+ case Prim_Triangles:
+ prim = GL_TRIANGLES;
+ break;
+ case Prim_Lines:
+ prim = GL_LINES;
+ break;
+ case Prim_TriangleStrip:
+ prim = GL_TRIANGLE_STRIP;
+ break;
+ default:
+ assert(0);
+ return;
+ }
+
+ fill->Set();
+ if (shaders->ProjLoc >= 0)
+ glUniformMatrix4fv(shaders->ProjLoc, 1, 0, &Proj.M[0][0]);
+ if (shaders->ViewLoc >= 0)
+ glUniformMatrix4fv(shaders->ViewLoc, 1, 0, &matrix.Transposed().M[0][0]);
+
+ if (shaders->UsesLighting && Lighting->Version != shaders->LightingVer)
+ {
+ shaders->LightingVer = Lighting->Version;
+ Lighting->Set(shaders);
+ }
+
+ glBindBuffer(GL_ARRAY_BUFFER, ((Buffer*)vertices)->GLBuffer);
+ for (int i = 0; i < 4; i++)
+ glEnableVertexAttribArray(i);
+
+ glVertexAttribPointer(0, 3, GL_FLOAT, false, sizeof(Vertex), (char*)offset + offsetof(Vertex, Pos));
+ glVertexAttribPointer(1, 4, GL_UNSIGNED_BYTE, true, sizeof(Vertex), (char*)offset + offsetof(Vertex, C));
+ glVertexAttribPointer(2, 2, GL_FLOAT, false, sizeof(Vertex), (char*)offset + offsetof(Vertex, U));
+ glVertexAttribPointer(3, 3, GL_FLOAT, false, sizeof(Vertex), (char*)offset + offsetof(Vertex, Norm));
+
+ if (indices)
+ {
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ((Buffer*)indices)->GLBuffer);
+ glDrawElements(prim, count, GL_UNSIGNED_SHORT, NULL);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+ }
+ else
+ {
+ glDrawArrays(prim, 0, count);
+ }
+
+ for (int i = 0; i < 4; i++)
+ glDisableVertexAttribArray(i);
+}
+
+void RenderDevice::SetLighting(const LightingParams* lt)
+{
+ Lighting = lt;
+}
+
+Buffer::~Buffer()
+{
+ if (GLBuffer)
+ glDeleteBuffers(1, &GLBuffer);
+}
+
+bool Buffer::Data(int use, const void* buffer, size_t size)
+{
+ switch (use & Buffer_TypeMask)
+ {
+ case Buffer_Index: Use = GL_ELEMENT_ARRAY_BUFFER; break;
+ default: Use = GL_ARRAY_BUFFER; break;
+ }
+
+ if (!GLBuffer)
+ glGenBuffers(1, &GLBuffer);
+
+ int mode = GL_DYNAMIC_DRAW;
+ if (use & Buffer_ReadOnly)
+ mode = GL_STATIC_DRAW;
+
+ glBindBuffer(Use, GLBuffer);
+ glBufferData(Use, size, buffer, mode);
+ glBindBuffer(Use, 0);
+ return 1;
+}
+
+void* Buffer::Map(size_t start, size_t size, int flags)
+{
+ int mode = GL_WRITE_ONLY;
+ //if (flags & Map_Unsynchronized)
+ // mode |= GL_MAP_UNSYNCHRONIZED;
+
+ glBindBuffer(Use, GLBuffer);
+ void* v = glMapBuffer(Use, mode);
+ glBindBuffer(Use, 0);
+ return v;
+}
+
+bool Buffer::Unmap(void*)
+{
+ glBindBuffer(Use, GLBuffer);
+ int r = glUnmapBuffer(Use);
+ glBindBuffer(Use, 0);
+ return r;
+}
+
+bool Shader::Compile(const char* src)
+{
+ if (!GLShader)
+ GLShader = glCreateShader(GLStage());
+
+ glShaderSource(GLShader, 1, &src, 0);
+ glCompileShader(GLShader);
+ GLint r;
+ glGetShaderiv(GLShader, GL_COMPILE_STATUS, &r);
+ if (!r)
+ {
+ GLchar msg[1024];
+ glGetShaderInfoLog(GLShader, sizeof(msg), 0, msg);
+ if (msg[0])
+ OVR_DEBUG_LOG(("Compiling shader\n%s\nfailed: %s\n", src, msg));
+ if (!r)
+ return 0;
+ }
+ return 1;
+}
+
+ShaderSet::ShaderSet()
+{
+ Prog = glCreateProgram();
+}
+ShaderSet::~ShaderSet()
+{
+ glDeleteProgram(Prog);
+}
+
+bool ShaderSet::Link()
+{
+ glBindAttribLocation(Prog, 0, "Position");
+ glBindAttribLocation(Prog, 1, "Color");
+ glBindAttribLocation(Prog, 2, "TexCoord");
+ glBindAttribLocation(Prog, 3, "Normal");
+
+ glLinkProgram(Prog);
+ GLint r;
+ glGetProgramiv(Prog, GL_LINK_STATUS, &r);
+ if (!r)
+ {
+ GLchar msg[1024];
+ glGetProgramInfoLog(Prog, sizeof(msg), 0, msg);
+ OVR_DEBUG_LOG(("Linking shaders failed: %s\n", msg));
+ if (!r)
+ return 0;
+ }
+ glUseProgram(Prog);
+
+ UniformInfo.Clear();
+ LightingVer = 0;
+ UsesLighting = 0;
+ GLuint i = 0;
+ for(;; i++)
+ {
+ GLsizei namelen;
+ GLint size = 0;
+ GLenum type;
+ GLchar name[32];
+ glGetActiveUniform(Prog, i, sizeof(name), &namelen, &size, &type, name);
+ if (size)
+ {
+ int l = glGetUniformLocation(Prog, name);
+ char *np = name;
+ while (*np)
+ {
+ if (*np == '[')
+ *np = 0;
+ np++;
+ }
+ Uniform u;
+ u.Name = name;
+ u.Location = l;
+ u.Size = size;
+ switch (type)
+ {
+ case GL_FLOAT: u.Type = 1; break;
+ case GL_FLOAT_VEC2: u.Type = 2; break;
+ case GL_FLOAT_VEC3: u.Type = 3; break;
+ case GL_FLOAT_VEC4: u.Type = 4; break;
+ case GL_FLOAT_MAT4: u.Type = 16; break;
+ default:
+ continue;
+ }
+ UniformInfo.PushBack(u);
+ if (!strcmp(name, "LightCount"))
+ UsesLighting = 1;
+ }
+ else
+ break;
+ }
+
+ ProjLoc = glGetUniformLocation(Prog, "Proj");
+ ViewLoc = glGetUniformLocation(Prog, "View");
+ for (int i = 0; i < 8; i++)
+ {
+ char texv[32];
+ sprintf(texv, "Texture%d", i);
+ TexLoc[i] = glGetUniformLocation(Prog, texv);
+ if (TexLoc[i] < 0)
+ break;
+
+ glUniform1i(TexLoc[i], i);
+ }
+ if (UsesLighting)
+ OVR_ASSERT(ProjLoc >= 0 && ViewLoc >= 0);
+ return 1;
+}
+
+void ShaderSet::Set(PrimitiveType) const
+{
+ glUseProgram(Prog);
+}
+
+bool ShaderSet::SetUniform(const char* name, int n, const float* v)
+{
+ for (int i = 0; i < UniformInfo.GetSize(); i++)
+ if (!strcmp(UniformInfo[i].Name.ToCStr(), name))
+ {
+ OVR_ASSERT(UniformInfo[i].Location >= 0);
+ glUseProgram(Prog);
+ switch (UniformInfo[i].Type)
+ {
+ case 1: glUniform1fv(UniformInfo[i].Location, n, v); break;
+ case 2: glUniform2fv(UniformInfo[i].Location, n/2, v); break;
+ case 3: glUniform3fv(UniformInfo[i].Location, n/3, v); break;
+ case 4: glUniform4fv(UniformInfo[i].Location, n/4, v); break;
+ default: OVR_ASSERT(0);
+ }
+ return 1;
+ }
+
+ OVR_DEBUG_LOG(("Warning: uniform %s not present in selected shader", name));
+ return 0;
+}
+
+bool ShaderSet::SetUniform4x4f(const char* name, const Matrix4f& m)
+{
+ for (int i = 0; i < UniformInfo.GetSize(); i++)
+ if (!strcmp(UniformInfo[i].Name.ToCStr(), name))
+ {
+ glUseProgram(Prog);
+ glUniformMatrix4fv(UniformInfo[i].Location, 1, 1, &m.M[0][0]);
+ return 1;
+ }
+
+ OVR_DEBUG_LOG(("Warning: uniform %s not present in selected shader", name));
+ return 0;
+}
+
+Texture::Texture(RenderDevice* r, int w, int h) : Ren(r), Width(w), Height(h)
+{
+ glGenTextures(1, &TexId);
+}
+
+Texture::~Texture()
+{
+ if (TexId)
+ glDeleteTextures(1, &TexId);
+}
+
+void Texture::Set(int slot, RenderTiny::ShaderStage stage) const
+{
+ Ren->SetTexture(stage, slot, this);
+}
+
+void Texture::SetSampleMode(int sm)
+{
+ glBindTexture(GL_TEXTURE_2D, TexId);
+ switch (sm & Sample_FilterMask)
+ {
+ case Sample_Linear:
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 0);
+ break;
+
+ case Sample_Anisotropic:
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 8);
+ break;
+
+ case Sample_Nearest:
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 0);
+ break;
+ }
+
+ switch (sm & Sample_AddressMask)
+ {
+ case Sample_Repeat:
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+ break;
+
+ case Sample_Clamp:
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ break;
+
+ case Sample_ClampBorder:
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
+ break;
+ }
+ glBindTexture(GL_TEXTURE_2D, 0);
+}
+
+Texture* RenderDevice::CreateTexture(int format, int width, int height, const void* data, int mipcount)
+{
+ GLenum glformat, gltype = GL_UNSIGNED_BYTE;
+ switch(format & Texture_TypeMask)
+ {
+ case Texture_RGBA: glformat = GL_RGBA; break;
+ case Texture_Depth: glformat = GL_DEPTH; gltype = GL_DEPTH_COMPONENT; break;
+ default:
+ return NULL;
+ }
+ Texture* NewTex = new Texture(this, width, height);
+ glBindTexture(GL_TEXTURE_2D, NewTex->TexId);
+ glGetError();
+
+ glTexImage2D(GL_TEXTURE_2D, 0, glformat, width, height, 0, glformat, gltype, data);
+ OVR_ASSERT(!glGetError());
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+
+ if (format == (Texture_RGBA|Texture_GenMipmaps)) // not render target
+ {
+ int srcw = width, srch = height;
+ int level = 0;
+ UByte* mipmaps = NULL;
+ do
+ {
+ level++;
+ int mipw = srcw >> 1; if (mipw < 1) mipw = 1;
+ int miph = srch >> 1; if (miph < 1) miph = 1;
+ if (mipmaps == NULL)
+ mipmaps = (UByte*)OVR_ALLOC(mipw * miph * 4);
+ FilterRgba2x2(level == 1 ? (const UByte*)data : mipmaps, srcw, srch, mipmaps);
+ glTexImage2D(GL_TEXTURE_2D, level, glformat, mipw, miph, 0, glformat, gltype, mipmaps);
+ OVR_ASSERT(!glGetError());
+ srcw = mipw;
+ srch = miph;
+ } while (srcw > 1 || srch > 1);
+ if (mipmaps)
+ OVR_FREE(mipmaps);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, level);
+ OVR_ASSERT(!glGetError());
+ }
+ else
+ {
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, mipcount-1);
+ OVR_ASSERT(!glGetError());
+ }
+
+ OVR_ASSERT(!glGetError());
+ glBindTexture(GL_TEXTURE_2D, 0);
+ return NewTex;
+}
+
+RBuffer::RBuffer(GLenum format, GLint w, GLint h)
+{
+ Width = w;
+ Height = h;
+ glGenRenderbuffersEXT(1, &BufId);
+ glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, BufId);
+ glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, format, w, h);
+ glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0);
+}
+
+RBuffer::~RBuffer()
+{
+ glDeleteRenderbuffersEXT(1, &BufId);
+}
+
+}}}
diff --git a/Samples/OculusRoomTiny/RenderTiny_GL_Device.h b/Samples/OculusRoomTiny/RenderTiny_GL_Device.h
new file mode 100644
index 0000000..feb504f
--- /dev/null
+++ b/Samples/OculusRoomTiny/RenderTiny_GL_Device.h
@@ -0,0 +1,228 @@
+/************************************************************************************
+
+Filename : RenderTiny_GL_Device.h
+Content : RenderDevice implementation header for OpenGL (tiny version)
+Created : September 10, 2012
+Authors : Andrew Reisse, Artem Bolgar
+
+Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+************************************************************************************/
+
+#ifndef OVR_Render_GL_Device_h
+#define OVR_Render_GL_Device_h
+
+#include "RenderTiny_Device.h"
+
+#if defined(OVR_OS_WIN32)
+#include <Windows.h>
+#endif
+
+#if defined(OVR_OS_MAC)
+#include <OpenGL/gl.h>
+#include <OpenGL/glext.h>
+#else
+#define GL_GLEXT_PROTOTYPES
+#include <GL/gl.h>
+#include <GL/glext.h>
+#endif
+
+namespace OVR { namespace RenderTiny { namespace GL {
+
+class RenderDevice;
+
+class Buffer : public RenderTiny::Buffer
+{
+public:
+ RenderDevice* Ren;
+ size_t Size;
+ GLenum Use;
+ GLuint GLBuffer;
+
+public:
+ Buffer(RenderDevice* r) : Ren(r), Size(0), Use(0), GLBuffer(0) {}
+ ~Buffer();
+
+ GLuint GetBuffer() { return GLBuffer; }
+
+ virtual size_t GetSize() { return Size; }
+ virtual void* Map(size_t start, size_t size, int flags = 0);
+ virtual bool Unmap(void *m);
+ virtual bool Data(int use, const void* buffer, size_t size);
+};
+
+class Texture : public RenderTiny::Texture
+{
+public:
+ RenderDevice* Ren;
+ GLuint TexId;
+ int Width, Height;
+
+ Texture(RenderDevice* r, int w, int h);
+ ~Texture();
+
+ virtual int GetWidth() const { return Width; }
+ virtual int GetHeight() const { return Height; }
+
+ virtual void SetSampleMode(int);
+
+ virtual void Set(int slot, ShaderStage stage = Shader_Fragment) const;
+};
+
+class Shader : public RenderTiny::Shader
+{
+public:
+ GLuint GLShader;
+
+ Shader(RenderDevice*, ShaderStage st, GLuint s) : RenderTiny::Shader(st), GLShader(s) {}
+ Shader(RenderDevice*, ShaderStage st, const char* src) : RenderTiny::Shader(st), GLShader(0)
+ {
+ Compile(src);
+ }
+ ~Shader()
+ {
+ if (GLShader)
+ glDeleteShader(GLShader);
+ }
+ bool Compile(const char* src);
+
+ GLenum GLStage() const
+ {
+ switch (Stage)
+ {
+ default: OVR_ASSERT(0); return GL_NONE;
+ case Shader_Vertex: return GL_VERTEX_SHADER;
+ case Shader_Fragment: return GL_FRAGMENT_SHADER;
+ }
+ }
+
+ //void Set(PrimitiveType prim) const;
+ //void SetUniformBuffer(Render::Buffer* buffers, int i = 0);
+};
+
+class ShaderSet : public RenderTiny::ShaderSet
+{
+public:
+ GLuint Prog;
+
+ struct Uniform
+ {
+ String Name;
+ int Location, Size;
+ int Type; // currently number of floats in vector
+ };
+ Array<Uniform> UniformInfo;
+
+ int ProjLoc, ViewLoc;
+ int TexLoc[8];
+ bool UsesLighting;
+ int LightingVer;
+
+ ShaderSet();
+ ~ShaderSet();
+
+ virtual void SetShader(RenderTiny::Shader *s)
+ {
+ Shaders[s->GetStage()] = s;
+ Shader* gls = (Shader*)s;
+ glAttachShader(Prog, gls->GLShader);
+ if (Shaders[Shader_Vertex] && Shaders[Shader_Fragment])
+ Link();
+ }
+ virtual void UnsetShader(int stage)
+ {
+ Shader* gls = (Shader*)(RenderTiny::Shader*)Shaders[stage];
+ if (gls)
+ glDetachShader(Prog, gls->GLShader);
+ Shaders[stage] = NULL;
+ Link();
+ }
+
+ virtual void Set(PrimitiveType prim) const;
+
+ // Set a uniform (other than the standard matrices). It is undefined whether the
+ // uniforms from one shader occupy the same space as those in other shaders
+ // (unless a buffer is used, then each buffer is independent).
+ virtual bool SetUniform(const char* name, int n, const float* v);
+ virtual bool SetUniform4x4f(const char* name, const Matrix4f& m);
+
+ bool Link();
+};
+
+ class RBuffer : public RefCountBase<RBuffer>
+{
+ public:
+ int Width, Height;
+ GLuint BufId;
+
+ RBuffer(GLenum format, GLint w, GLint h);
+ ~RBuffer();
+};
+
+class RenderDevice : public RenderTiny::RenderDevice
+{
+ Ptr<Shader> VertexShaders[VShader_Count];
+ Ptr<Shader> FragShaders[FShader_Count];
+
+ Ptr<ShaderFill> DefaultFill;
+
+ Matrix4f Proj;
+
+ Ptr<Texture> CurRenderTarget;
+ Array<Ptr<RBuffer> > DepthBuffers;
+ GLuint CurrentFbo;
+
+ const LightingParams* Lighting;
+
+
+public:
+ RenderDevice(const RendererParams& p);
+
+ virtual void SetRealViewport(const Viewport& vp);
+
+ //virtual void SetScissor(int x, int y, int w, int h);
+
+ virtual void Clear(float r = 0, float g = 0, float b = 0, float a = 1, float depth = 1);
+ virtual void Rect(float left, float top, float right, float bottom) { OVR_UNUSED4(left,top,right,bottom); }
+
+ virtual void BeginRendering();
+ virtual void SetDepthMode(bool enable, bool write, CompareFunc func = Compare_Less);
+ virtual void SetWorldUniforms(const Matrix4f& proj);
+
+ RBuffer* GetDepthBuffer(int w, int h, int ms);
+
+ virtual void SetRenderTarget(RenderTiny::Texture* color,
+ RenderTiny::Texture* depth = NULL, RenderTiny::Texture* stencil = NULL);
+
+ virtual void SetLighting(const LightingParams* lt);
+
+ virtual void Render(const Matrix4f& matrix, Model* model);
+ virtual void Render(const ShaderFill* fill, RenderTiny::Buffer* vertices, RenderTiny::Buffer* indices,
+ const Matrix4f& matrix, int offset, int count, PrimitiveType prim = Prim_Triangles);
+
+ virtual Buffer* CreateBuffer();
+ virtual Texture* CreateTexture(int format, int width, int height, const void* data, int mipcount=1);
+ virtual ShaderSet* CreateShaderSet() { return new ShaderSet; }
+
+ virtual ShaderFill *CreateSimpleFill() { return DefaultFill; }
+
+ virtual Shader *LoadBuiltinShader(ShaderStage stage, int shader);
+
+ void SetTexture(RenderTiny::ShaderStage, int slot, const Texture* t);
+};
+
+}}}
+
+#endif
diff --git a/Samples/OculusRoomTiny/Win32_OculusRoomTiny.cpp b/Samples/OculusRoomTiny/Win32_OculusRoomTiny.cpp
new file mode 100644
index 0000000..2e6dd3e
--- /dev/null
+++ b/Samples/OculusRoomTiny/Win32_OculusRoomTiny.cpp
@@ -0,0 +1,712 @@
+/************************************************************************************
+
+Filename : Win32_OculusRoomTiny.cpp
+Content : First-person view test application for Oculus Rift
+Created : October 4, 2012
+Authors : Michael Antonov, Andrew Reisse
+
+Copyright : Copyright 2012 Oculus, Inc. All Rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+*************************************************************************************/
+
+#include "Win32_OculusRoomTiny.h"
+#include "RenderTiny_D3D1X_Device.h"
+
+//-------------------------------------------------------------------------------------
+// ***** OculusRoomTiny Class
+
+// Static pApp simplifies routing the window function.
+OculusRoomTinyApp* OculusRoomTinyApp::pApp = 0;
+
+
+OculusRoomTinyApp::OculusRoomTinyApp(HINSTANCE hinst)
+ : pRender(0),
+ LastUpdate(0),
+
+ // Win32
+ hWnd(NULL),
+ hInstance(hinst), Quit(0), MouseCaptured(true),
+ hXInputModule(0), pXInputGetState(0),
+
+ // Initial location
+ EyePos(0.0f, 1.6f, -5.0f),
+ EyeYaw(YawInitial), EyePitch(0), EyeRoll(0),
+ LastSensorYaw(0),
+ SConfig(),
+ PostProcess(PostProcess_Distortion),
+ ShiftDown(false),
+ ControlDown(false)
+{
+ pApp = this;
+
+ Width = 1280;
+ Height = 800;
+
+ StartupTicks = OVR::Timer::GetTicks();
+ LastPadPacketNo = 0;
+
+ MoveForward = MoveBack = MoveLeft = MoveRight = 0;
+ GamepadMove = Vector3f(0);
+ GamepadRotate = Vector3f(0);
+}
+
+OculusRoomTinyApp::~OculusRoomTinyApp()
+{
+ RemoveHandlerFromDevices();
+ pSensor.Clear();
+ pHMD.Clear();
+ destroyWindow();
+ pApp = 0;
+}
+
+
+int OculusRoomTinyApp::OnStartup(const char* args)
+{
+ OVR_UNUSED(args);
+
+
+ // *** Oculus HMD & Sensor Initialization
+
+ // Create DeviceManager and first available HMDDevice from it.
+ // Sensor object is created from the HMD, to ensure that it is on the
+ // correct device.
+
+ pManager = *DeviceManager::Create();
+
+ // We'll handle it's messages in this case.
+ pManager->SetMessageHandler(this);
+
+
+ int detectionResult = IDCONTINUE;
+ const char* detectionMessage;
+
+ do
+ {
+ // Release Sensor/HMD in case this is a retry.
+ pSensor.Clear();
+ pHMD.Clear();
+ RenderParams.MonitorName.Clear();
+
+ pHMD = *pManager->EnumerateDevices<HMDDevice>().CreateDevice();
+ if (pHMD)
+ {
+ pSensor = *pHMD->GetSensor();
+
+ // This will initialize HMDInfo with information about configured IPD,
+ // screen size and other variables needed for correct projection.
+ // We pass HMD DisplayDeviceName into the renderer to select the
+ // correct monitor in full-screen mode.
+ if (pHMD->GetDeviceInfo(&HMDInfo))
+ {
+ RenderParams.MonitorName = HMDInfo.DisplayDeviceName;
+ RenderParams.DisplayId = HMDInfo.DisplayId;
+ SConfig.SetHMDInfo(HMDInfo);
+ }
+ }
+ else
+ {
+ // If we didn't detect an HMD, try to create the sensor directly.
+ // This is useful for debugging sensor interaction; it is not needed in
+ // a shipping app.
+ pSensor = *pManager->EnumerateDevices<SensorDevice>().CreateDevice();
+ }
+
+
+ // If there was a problem detecting the Rift, display appropriate message.
+ detectionResult = IDCONTINUE;
+
+ if (!pHMD && !pSensor)
+ detectionMessage = "Oculus Rift not detected.";
+ else if (!pHMD)
+ detectionMessage = "Oculus Sensor detected; HMD Display not detected.";
+ else if (!pSensor)
+ detectionMessage = "Oculus HMD Display detected; Sensor not detected.";
+ else if (HMDInfo.DisplayDeviceName[0] == '\0')
+ detectionMessage = "Oculus Sensor detected; HMD display EDID not detected.";
+ else
+ detectionMessage = 0;
+
+ if (detectionMessage)
+ {
+ String messageText(detectionMessage);
+ messageText += "\n\n"
+ "Press 'Try Again' to run retry detection.\n"
+ "Press 'Continue' to run full-screen anyway.";
+
+ detectionResult = ::MessageBoxA(0, messageText.ToCStr(), "Oculus Rift Detection",
+ MB_CANCELTRYCONTINUE|MB_ICONWARNING);
+
+ if (detectionResult == IDCANCEL)
+ return 1;
+ }
+
+ } while (detectionResult != IDCONTINUE);
+
+
+ if (HMDInfo.HResolution > 0)
+ {
+ Width = HMDInfo.HResolution;
+ Height = HMDInfo.VResolution;
+ }
+
+
+ if (!setupWindow())
+ return 1;
+
+ if (pSensor)
+ {
+ // We need to attach sensor to SensorFusion object for it to receive
+ // body frame messages and update orientation. SFusion.GetOrientation()
+ // is used in OnIdle() to orient the view.
+ SFusion.AttachToSensor(pSensor);
+ SFusion.SetDelegateMessageHandler(this);
+ SFusion.SetPredictionEnabled(true);
+ }
+
+
+ // *** Initialize Rendering
+
+ // Enable multi-sampling by default.
+ RenderParams.Multisample = 4;
+ RenderParams.Fullscreen = true;
+
+ // Setup Graphics.
+ pRender = *RenderTiny::D3D10::RenderDevice::CreateDevice(RenderParams, (void*)hWnd);
+ if (!pRender)
+ return 1;
+
+
+ // *** Configure Stereo settings.
+
+ SConfig.SetFullViewport(Viewport(0,0, Width, Height));
+ SConfig.SetStereoMode(Stereo_LeftRight_Multipass);
+
+ // Configure proper Distortion Fit.
+ // For 7" screen, fit to touch left side of the view, leaving a bit of invisible
+ // screen on the top (saves on rendering cost).
+ // For smaller screens (5.5"), fit to the top.
+ if (HMDInfo.HScreenSize > 0.0f)
+ {
+ if (HMDInfo.HScreenSize > 0.140f) // 7"
+ SConfig.SetDistortionFitPointVP(-1.0f, 0.0f);
+ else
+ SConfig.SetDistortionFitPointVP(0.0f, 1.0f);
+ }
+
+ pRender->SetSceneRenderScale(SConfig.GetDistortionScale());
+
+ SConfig.Set2DAreaFov(DegreeToRad(85.0f));
+
+
+ // *** Populate Room Scene
+
+ // This creates lights and models.
+ PopulateRoomScene(&Scene, pRender);
+
+
+ LastUpdate = GetAppTime();
+ return 0;
+}
+
+void OculusRoomTinyApp::OnMessage(const Message& msg)
+{
+ if (msg.Type == Message_DeviceAdded && msg.pDevice == pManager)
+ {
+ LogText("DeviceManager reported device added.\n");
+ }
+ else if (msg.Type == Message_DeviceRemoved && msg.pDevice == pManager)
+ {
+ LogText("DeviceManager reported device removed.\n");
+ }
+ else if (msg.Type == Message_DeviceAdded && msg.pDevice == pSensor)
+ {
+ LogText("Sensor reported device added.\n");
+ }
+ else if (msg.Type == Message_DeviceRemoved && msg.pDevice == pSensor)
+ {
+ LogText("Sensor reported device removed.\n");
+ }
+}
+
+
+void OculusRoomTinyApp::OnGamepad(float padLx, float padLy, float padRx, float padRy)
+{
+ GamepadMove = Vector3f(padLx * padLx * (padLx > 0 ? 1 : -1),
+ 0,
+ padLy * padLy * (padLy > 0 ? -1 : 1));
+ GamepadRotate = Vector3f(2 * padRx, -2 * padRy, 0);
+}
+
+void OculusRoomTinyApp::OnMouseMove(int x, int y, int modifiers)
+{
+ OVR_UNUSED(modifiers);
+
+ // Mouse motion here is always relative.
+ int dx = x, dy = y;
+ const float maxPitch = ((3.1415f/2)*0.98f);
+
+ // Apply to rotation. Subtract for right body frame rotation,
+ // since yaw rotation is positive CCW when looking down on XZ plane.
+ EyeYaw -= (Sensitivity * dx)/ 360.0f;
+
+ if (!pSensor)
+ {
+ EyePitch -= (Sensitivity * dy)/ 360.0f;
+
+ if (EyePitch > maxPitch)
+ EyePitch = maxPitch;
+ if (EyePitch < -maxPitch)
+ EyePitch = -maxPitch;
+ }
+}
+
+void OculusRoomTinyApp::OnKey(unsigned vk, bool down)
+{
+ switch (vk)
+ {
+ case 'Q':
+ if (down && ControlDown)
+ Quit = true;
+ break;
+ case VK_ESCAPE:
+ if (!down)
+ Quit = true;
+ break;
+
+ // Handle player movement keys.
+ // We just update movement state here, while the actual translation is done in OnIdle()
+ // based on time.
+ case 'W': MoveForward = down ? (MoveForward | 1) : (MoveForward & ~1); break;
+ case 'S': MoveBack = down ? (MoveBack | 1) : (MoveBack & ~1); break;
+ case 'A': MoveLeft = down ? (MoveLeft | 1) : (MoveLeft & ~1); break;
+ case 'D': MoveRight = down ? (MoveRight | 1) : (MoveRight & ~1); break;
+ case VK_UP: MoveForward = down ? (MoveForward | 2) : (MoveForward & ~2); break;
+ case VK_DOWN: MoveBack = down ? (MoveBack | 2) : (MoveBack & ~2); break;
+
+ case 'R':
+ SFusion.Reset();
+ break;
+
+ case 'P':
+ if (down)
+ {
+ // Toggle chromatic aberration correction on/off.
+ RenderDevice::PostProcessShader shader = pRender->GetPostProcessShader();
+
+ if (shader == RenderDevice::PostProcessShader_Distortion)
+ {
+ pRender->SetPostProcessShader(RenderDevice::PostProcessShader_DistortionAndChromAb);
+ }
+ else if (shader == RenderDevice::PostProcessShader_DistortionAndChromAb)
+ {
+ pRender->SetPostProcessShader(RenderDevice::PostProcessShader_Distortion);
+ }
+ else
+ OVR_ASSERT(false);
+ }
+ break;
+
+ // Switch rendering modes/distortion.
+ case VK_F1:
+ SConfig.SetStereoMode(Stereo_None);
+ PostProcess = PostProcess_None;
+ break;
+ case VK_F2:
+ SConfig.SetStereoMode(Stereo_LeftRight_Multipass);
+ PostProcess = PostProcess_None;
+ break;
+ case VK_F3:
+ SConfig.SetStereoMode(Stereo_LeftRight_Multipass);
+ PostProcess = PostProcess_Distortion;
+ break;
+
+ // Stereo IPD adjustments, in meter (default IPD is 64mm).
+ case VK_OEM_PLUS:
+ case VK_INSERT:
+ if (down)
+ SConfig.SetIPD(SConfig.GetIPD() + 0.0005f * (ShiftDown ? 5.0f : 1.0f));
+ break;
+ case VK_OEM_MINUS:
+ case VK_DELETE:
+ if (down)
+ SConfig.SetIPD(SConfig.GetIPD() - 0.0005f * (ShiftDown ? 5.0f : 1.0f));
+ break;
+
+ // Holding down Shift key accelerates adjustment velocity.
+ case VK_SHIFT:
+ ShiftDown = down;
+ break;
+ case VK_CONTROL:
+ ControlDown = down;
+ break;
+ }
+}
+
+
+void OculusRoomTinyApp::OnIdle()
+{
+ double curtime = GetAppTime();
+ float dt = float(curtime - LastUpdate);
+ LastUpdate = curtime;
+
+
+ // Handle Sensor motion.
+ // We extract Yaw, Pitch, Roll instead of directly using the orientation
+ // to allow "additional" yaw manipulation with mouse/controller.
+ if (pSensor)
+ {
+ Quatf hmdOrient = SFusion.GetOrientation();
+ float yaw = 0.0f;
+
+ hmdOrient.GetEulerAngles<Axis_Y, Axis_X, Axis_Z>(&yaw, &EyePitch, &EyeRoll);
+
+ EyeYaw += (yaw - LastSensorYaw);
+ LastSensorYaw = yaw;
+ }
+
+
+ // Gamepad rotation.
+ EyeYaw -= GamepadRotate.x * dt;
+
+ if (!pSensor)
+ {
+ // Allow gamepad to look up/down, but only if there is no Rift sensor.
+ EyePitch -= GamepadRotate.y * dt;
+
+ const float maxPitch = ((3.1415f/2)*0.98f);
+ if (EyePitch > maxPitch)
+ EyePitch = maxPitch;
+ if (EyePitch < -maxPitch)
+ EyePitch = -maxPitch;
+ }
+
+ // Handle keyboard movement.
+ // This translates EyePos based on Yaw vector direction and keys pressed.
+ // Note that Pitch and Roll do not affect movement (they only affect view).
+ if (MoveForward || MoveBack || MoveLeft || MoveRight)
+ {
+ Vector3f localMoveVector(0,0,0);
+ Matrix4f yawRotate = Matrix4f::RotationY(EyeYaw);
+
+ if (MoveForward)
+ localMoveVector = ForwardVector;
+ else if (MoveBack)
+ localMoveVector = -ForwardVector;
+
+ if (MoveRight)
+ localMoveVector += RightVector;
+ else if (MoveLeft)
+ localMoveVector -= RightVector;
+
+ // Normalize vector so we don't move faster diagonally.
+ localMoveVector.Normalize();
+ Vector3f orientationVector = yawRotate.Transform(localMoveVector);
+ orientationVector *= MoveSpeed * dt * (ShiftDown ? 3.0f : 1.0f);
+
+ EyePos += orientationVector;
+ }
+
+ else if (GamepadMove.LengthSq() > 0)
+ {
+ Matrix4f yawRotate = Matrix4f::RotationY(EyeYaw);
+ Vector3f orientationVector = yawRotate.Transform(GamepadMove);
+ orientationVector *= MoveSpeed * dt;
+ EyePos += orientationVector;
+ }
+
+
+ // Rotate and position View Camera, using YawPitchRoll in BodyFrame coordinates.
+ //
+ Matrix4f rollPitchYaw = Matrix4f::RotationY(EyeYaw) * Matrix4f::RotationX(EyePitch) *
+ Matrix4f::RotationZ(EyeRoll);
+ Vector3f up = rollPitchYaw.Transform(UpVector);
+ Vector3f forward = rollPitchYaw.Transform(ForwardVector);
+
+
+ // Minimal head modelling.
+ float headBaseToEyeHeight = 0.15f; // Vertical height of eye from base of head
+ float headBaseToEyeProtrusion = 0.09f; // Distance forward of eye from base of head
+
+ Vector3f eyeCenterInHeadFrame(0.0f, headBaseToEyeHeight, -headBaseToEyeProtrusion);
+ Vector3f shiftedEyePos = EyePos + rollPitchYaw.Transform(eyeCenterInHeadFrame);
+ shiftedEyePos.y -= eyeCenterInHeadFrame.y; // Bring the head back down to original height
+
+ View = Matrix4f::LookAtRH(shiftedEyePos, shiftedEyePos + forward, up);
+
+ // This is what transformation would be without head modeling.
+ // View = Matrix4f::LookAtRH(EyePos, EyePos + forward, up);
+
+ switch(SConfig.GetStereoMode())
+ {
+ case Stereo_None:
+ Render(SConfig.GetEyeRenderParams(StereoEye_Center));
+ break;
+
+ case Stereo_LeftRight_Multipass:
+ Render(SConfig.GetEyeRenderParams(StereoEye_Left));
+ Render(SConfig.GetEyeRenderParams(StereoEye_Right));
+ break;
+ }
+
+ pRender->Present();
+ // Force GPU to flush the scene, resulting in the lowest possible latency.
+ pRender->ForceFlushGPU();
+}
+
+
+// Render the scene for one eye.
+void OculusRoomTinyApp::Render(const StereoEyeParams& stereo)
+{
+ pRender->BeginScene(PostProcess);
+
+ // Apply Viewport/Projection for the eye.
+ pRender->ApplyStereoParams(stereo);
+ pRender->Clear();
+ pRender->SetDepthMode(true, true);
+
+ Scene.Render(pRender, stereo.ViewAdjust * View);
+
+ pRender->FinishScene();
+}
+
+
+//-------------------------------------------------------------------------------------
+// ***** Win32-Specific Logic
+
+bool OculusRoomTinyApp::setupWindow()
+{
+
+ WNDCLASS wc;
+ memset(&wc, 0, sizeof(wc));
+ wc.lpszClassName = L"OVRAppWindow";
+ wc.style = CS_OWNDC;
+ wc.lpfnWndProc = systemWindowProc;
+ wc.cbWndExtra = sizeof(OculusRoomTinyApp*);
+ RegisterClass(&wc);
+
+
+ RECT winSize = { 0, 0, Width, Height };
+ AdjustWindowRect(&winSize, WS_POPUP, false);
+ hWnd = CreateWindowA("OVRAppWindow", "OculusRoomTiny", WS_POPUP|WS_VISIBLE,
+ HMDInfo.DesktopX, HMDInfo.DesktopY,
+ winSize.right-winSize.left, winSize.bottom-winSize.top,
+ NULL, NULL, hInstance, (LPVOID)this);
+
+
+ // Initialize Window center in screen coordinates
+ POINT center = { Width / 2, Height / 2 };
+ ::ClientToScreen(hWnd, &center);
+ WindowCenter = center;
+
+
+ return (hWnd != NULL);
+}
+
+void OculusRoomTinyApp::destroyWindow()
+{
+ pRender.Clear();
+
+ if (hWnd)
+ {
+ // Release window resources.
+ ::DestroyWindow(hWnd);
+ UnregisterClass(L"OVRAppWindow", hInstance);
+ hWnd = 0;
+ Width = Height = 0;
+ }
+}
+
+
+LRESULT CALLBACK OculusRoomTinyApp::systemWindowProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
+{
+ if (msg == WM_NCCREATE)
+ pApp->hWnd = hwnd;
+ return pApp->windowProc(msg, wp, lp);
+}
+
+void OculusRoomTinyApp::giveUsFocus(bool setFocus)
+{
+ if (setFocus)
+ {
+ ::SetCursorPos(WindowCenter.x, WindowCenter.y);
+
+ MouseCaptured = true;
+ ::SetCapture(hWnd);
+ ::ShowCursor(FALSE);
+
+ }
+ else
+ {
+ MouseCaptured = false;
+ ::ReleaseCapture();
+ ::ShowCursor(TRUE);
+ }
+}
+
+LRESULT OculusRoomTinyApp::windowProc(UINT msg, WPARAM wp, LPARAM lp)
+{
+ switch (msg)
+ {
+ case WM_MOUSEMOVE:
+ {
+ if (MouseCaptured)
+ {
+ // Convert mouse motion to be relative (report the offset and re-center).
+ POINT newPos = { LOWORD(lp), HIWORD(lp) };
+ ::ClientToScreen(hWnd, &newPos);
+ if ((newPos.x == WindowCenter.x) && (newPos.y == WindowCenter.y))
+ break;
+ ::SetCursorPos(WindowCenter.x, WindowCenter.y);
+
+ LONG dx = newPos.x - WindowCenter.x;
+ LONG dy = newPos.y - WindowCenter.y;
+ pApp->OnMouseMove(dx, dy, 0);
+ }
+ }
+ break;
+
+ case WM_MOVE:
+ {
+ RECT r;
+ GetClientRect(hWnd, &r);
+ WindowCenter.x = r.right/2;
+ WindowCenter.y = r.bottom/2;
+ ::ClientToScreen(hWnd, &WindowCenter);
+ }
+ break;
+
+ case WM_KEYDOWN:
+ OnKey((unsigned)wp, true);
+ break;
+ case WM_KEYUP:
+ OnKey((unsigned)wp, false);
+ break;
+
+ case WM_SETFOCUS:
+ giveUsFocus(true);
+ break;
+
+ case WM_KILLFOCUS:
+ giveUsFocus(false);
+ break;
+
+ case WM_CREATE:
+ // Hack to position mouse in fullscreen window shortly after startup.
+ SetTimer(hWnd, 0, 100, NULL);
+ break;
+
+ case WM_TIMER:
+ KillTimer(hWnd, 0);
+ giveUsFocus(true);
+ break;
+
+ case WM_QUIT:
+ case WM_CLOSE:
+ Quit = true;
+ return 0;
+ }
+
+ return DefWindowProc(hWnd, msg, wp, lp);
+}
+
+static inline float GamepadStick(short in)
+{
+ float v;
+ if (abs(in) < 9000)
+ return 0;
+ else if (in > 9000)
+ v = (float) in - 9000;
+ else
+ v = (float) in + 9000;
+ return v / (32767 - 9000);
+}
+
+static inline float GamepadTrigger(BYTE in)
+{
+ return (in < 30) ? 0.0f : (float(in-30) / 225);
+}
+
+
+int OculusRoomTinyApp::Run()
+{
+ // Loop processing messages until Quit flag is set,
+ // rendering game scene inside of OnIdle().
+
+ while (!Quit)
+ {
+ MSG msg;
+ if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
+ {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+ else
+ {
+ // Read game-pad.
+ XINPUT_STATE xis;
+
+ if (pXInputGetState && !pXInputGetState(0, &xis) &&
+ (xis.dwPacketNumber != LastPadPacketNo))
+ {
+ OnGamepad(GamepadStick(xis.Gamepad.sThumbLX),
+ GamepadStick(xis.Gamepad.sThumbLY),
+ GamepadStick(xis.Gamepad.sThumbRX),
+ GamepadStick(xis.Gamepad.sThumbRY));
+ //pad.LT = GamepadTrigger(xis.Gamepad.bLeftTrigger);
+ LastPadPacketNo = xis.dwPacketNumber;
+ }
+
+ pApp->OnIdle();
+
+ // Keep sleeping when we're minimized.
+ if (IsIconic(hWnd))
+ Sleep(10);
+ }
+ }
+
+ return 0;
+}
+
+
+//-------------------------------------------------------------------------------------
+// ***** Program Startup
+
+int WINAPI WinMain(HINSTANCE hinst, HINSTANCE, LPSTR inArgs, int)
+{
+ int exitCode = 0;
+
+ // Initializes LibOVR. This LogMask_All enables maximum logging.
+ // Custom allocator can also be specified here.
+ OVR::System::Init(OVR::Log::ConfigureDefaultLog(OVR::LogMask_All));
+
+ // Scope to force application destructor before System::Destroy.
+ {
+ OculusRoomTinyApp app(hinst);
+ //app.hInstance = hinst;
+
+ exitCode = app.OnStartup(inArgs);
+ if (!exitCode)
+ {
+ // Processes messages and calls OnIdle() to do rendering.
+ exitCode = app.Run();
+ }
+ }
+
+ // No OVR functions involving memory are allowed after this.
+ OVR::System::Destroy();
+
+ OVR_DEBUG_STATEMENT(_CrtDumpMemoryLeaks());
+ return exitCode;
+}
diff --git a/Samples/OculusRoomTiny/Win32_OculusRoomTiny.h b/Samples/OculusRoomTiny/Win32_OculusRoomTiny.h
new file mode 100644
index 0000000..5a0eedf
--- /dev/null
+++ b/Samples/OculusRoomTiny/Win32_OculusRoomTiny.h
@@ -0,0 +1,189 @@
+/************************************************************************************
+
+Filename : OculusRoomTiny.h
+Content : Simplest possible first-person view test application for Oculus Rift
+Created : March 10, 2012
+Authors : Michael Antonov, Andrew Reisse
+
+Copyright : Copyright 2012 Oculus, Inc. All Rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+*************************************************************************************/
+#ifndef INC_OculusRoomTiny_h
+#define INC_OculusRoomTiny_h
+
+#include <Windows.h>
+#include <xinput.h>
+
+#include "OVR.h"
+#include "Util/Util_Render_Stereo.h"
+#include "../../LibOVR/Src/Kernel/OVR_Timer.h"
+#include "RenderTiny_D3D1X_Device.h"
+
+using namespace OVR;
+using namespace OVR::RenderTiny;
+
+
+//-------------------------------------------------------------------------------------
+// ***** OculusRoomTiny Description
+
+// This app renders a simple flat-shaded room allowing the user to move along the
+// floor and look around with an HMD, mouse, keyboard and gamepad.
+// By default, the application will start full-screen on Oculus Rift.
+//
+// The following keys work:
+//
+// 'W', 'S', 'A', 'D' - Move forward, back; strafe left/right.
+// F1 - No stereo, no distortion.
+// F2 - Stereo, no distortion.
+// F3 - Stereo and distortion.
+//
+
+// The world RHS coordinate system is defines as follows (as seen in perspective view):
+// Y - Up
+// Z - Back
+// X - Right
+const Vector3f UpVector(0.0f, 1.0f, 0.0f);
+const Vector3f ForwardVector(0.0f, 0.0f, -1.0f);
+const Vector3f RightVector(1.0f, 0.0f, 0.0f);
+
+// We start out looking in the positive Z (180 degree rotation).
+const float YawInitial = 3.141592f;
+const float Sensitivity = 1.0f;
+const float MoveSpeed = 3.0f; // m/s
+
+
+//-------------------------------------------------------------------------------------
+// ***** OculusRoomTiny Application class
+
+// An instance of this class is created on application startup (main/WinMain).
+//
+// It then works as follows:
+//
+// OnStartup - Window, graphics and HMD setup is done here.
+// This function will initialize OVR::DeviceManager and HMD,
+// creating SensorDevice and attaching it to SensorFusion.
+// This needs to be done before obtaining sensor data.
+//
+// OnIdle - Does per-frame processing, processing SensorFusion and
+// movement input and rendering the frame.
+
+class OculusRoomTinyApp : public MessageHandler
+{
+public:
+ OculusRoomTinyApp(HINSTANCE hinst);
+ ~OculusRoomTinyApp();
+
+ // Initializes graphics, Rift input and creates world model.
+ virtual int OnStartup(const char* args);
+ // Called per frame to sample SensorFucion and render the world.
+ virtual void OnIdle();
+
+ // Installed for Oculus device messages. Optional.
+ virtual void OnMessage(const Message& msg);
+
+ // Handle input events for movement.
+ virtual void OnGamepad(float padLx, float padLY, float padRx, float padRy);
+ virtual void OnMouseMove(int x, int y, int modifiers);
+ virtual void OnKey(unsigned vk, bool down);
+
+ // Render the view for one eye.
+ void Render(const StereoEyeParams& stereo);
+
+ // Main application loop.
+ int Run();
+
+ // Return amount of time passed since application started in seconds.
+ double GetAppTime() const
+ {
+ return (OVR::Timer::GetTicks() - StartupTicks) * (1.0 / (double)OVR::Timer::MksPerSecond);
+ }
+
+
+protected:
+
+ // Win32 window setup interface.
+ LRESULT windowProc(UINT msg, WPARAM wp, LPARAM lp);
+ bool setupWindow();
+ void destroyWindow();
+ // Win32 static function that delegates to WindowProc member function.
+ static LRESULT CALLBACK systemWindowProc(HWND window, UINT msg, WPARAM wp, LPARAM lp);
+
+ void giveUsFocus(bool setFocus);
+
+ static OculusRoomTinyApp* pApp;
+
+ // *** Rendering Variables
+ Ptr<RenderDevice> pRender;
+ RendererParams RenderParams;
+ int Width, Height;
+
+
+ // *** Win32 System Variables
+ HWND hWnd;
+ HINSTANCE hInstance;
+ POINT WindowCenter; // In desktop coordinates
+ bool Quit;
+ bool MouseCaptured;
+
+ // Dynamically ink to XInput to simplify projects.
+ typedef DWORD (WINAPI *PFn_XInputGetState)(DWORD dwUserIndex, XINPUT_STATE* pState);
+ PFn_XInputGetState pXInputGetState;
+ HMODULE hXInputModule;
+ UInt32 LastPadPacketNo;
+
+
+ // *** Oculus HMD Variables
+
+ Ptr<DeviceManager> pManager;
+ Ptr<SensorDevice> pSensor;
+ Ptr<HMDDevice> pHMD;
+ SensorFusion SFusion;
+ OVR::HMDInfo HMDInfo;
+
+ // Last update seconds, used for move speed timing.
+ double LastUpdate;
+ UInt64 StartupTicks;
+
+ // Position and look. The following apply:
+ Vector3f EyePos;
+ float EyeYaw; // Rotation around Y, CCW positive when looking at RHS (X,Z) plane.
+ float EyePitch; // Pitch. If sensor is plugged in, only read from sensor.
+ float EyeRoll; // Roll, only accessible from Sensor.
+ float LastSensorYaw; // Stores previous Yaw value from to support computing delta.
+
+ // Movement state; different bits may be set based on the state of keys.
+ UByte MoveForward;
+ UByte MoveBack;
+ UByte MoveLeft;
+ UByte MoveRight;
+ Vector3f GamepadMove, GamepadRotate;
+
+ Matrix4f View;
+ RenderTiny::Scene Scene;
+
+ // Stereo view parameters.
+ StereoConfig SConfig;
+ PostProcessType PostProcess;
+
+ // Shift accelerates movement/adjustment velocity.
+ bool ShiftDown;
+ bool ControlDown;
+};
+
+// Adds sample models and lights to the argument scene.
+void PopulateRoomScene(Scene* scene, RenderDevice* render);
+
+
+#endif