mirror of
https://github.com/Cesarus85/ringfall-mjolnir-vr.git
synced 2026-10-22 00:00:30 +02:00
Merge feature/multiview: both eyes in one draw
GL_OVR_multiview2 (vr.multiview, on by default): the scene drawn once from between the eyes into a two-layer target, each view's positions moved to its eye in the vertex programs; the sky as though infinitely far, what the game projects itself by its depth, screen reads from a composite of both eyes. At one spot in 'Halo': both eyes 6.3 ms -> 4.0 ms, the frame's work 7.9 -> 5.8 ms. Also: no VR facing during cinematics, hands held still under the system menu. Merged with reservations; one-time diagnostics log what may still be off. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
commit
4e6b3f69f6
12 files changed
+673
-17
No files matched your search
@@ -124,6 +124,9 @@ int host_xr_copy_layer_region(int layer, unsigned int texture, int clear_alpha,
|
||||
/* submits the frame begun by host_xr_begin_frame */
|
||||
int host_xr_end_frame(const struct halo_xr_submit *submit);
|
||||
void host_xr_haptic(int hand, float amplitude, float seconds);
|
||||
/* copies a layer of a texture array (the eyes drawn at once, multiview)
|
||||
into a layer's image, opaque */
|
||||
int host_xr_copy_layer_array(int layer, unsigned int texture, int array_layer);
|
||||
/* darkens the eye images from their edges, strength 0..1 (0: off) */
|
||||
void host_xr_set_vignette(float strength);
|
||||
|
||||
|
||||
+112
-1
@@ -124,6 +124,13 @@ static struct
|
||||
GLint vignette_uniform;
|
||||
GLint tangents_uniform;
|
||||
float vignette;
|
||||
/* the same copy from a layer of a texture array (the eyes drawn at
|
||||
once, multiview): its program and uniforms, and the layer for the copy
|
||||
under way (host_xr_copy_layer_array; -1: a plain texture) */
|
||||
GLuint array_program;
|
||||
GLint array_source_uniform, array_rect_uniform, array_layer_uniform;
|
||||
GLint array_vignette_uniform, array_tangents_uniform;
|
||||
int copy_array_layer;
|
||||
GLuint vertex_array;
|
||||
GLuint sampler;
|
||||
} xr;
|
||||
@@ -248,6 +255,33 @@ static const char copy_fragment_shader[] =
|
||||
" }\n"
|
||||
"}\n";
|
||||
|
||||
static const char copy_array_fragment_shader[] =
|
||||
"#version 300 es\n"
|
||||
"precision highp float;\n"
|
||||
"uniform highp sampler2DArray source;\n"
|
||||
"uniform float layer;\n"
|
||||
"uniform float vignette;\n"
|
||||
"uniform vec4 tangents;\n"
|
||||
"in vec2 coordinate;\n"
|
||||
"in vec2 disc;\n"
|
||||
"out vec4 color;\n"
|
||||
"vec3 to_linear(vec3 c)\n"
|
||||
"{\n"
|
||||
" return mix(c / 12.92, pow((c + 0.055) / 1.055, vec3(2.4)), step(0.04045, c));\n"
|
||||
"}\n"
|
||||
"void main()\n"
|
||||
"{\n"
|
||||
" vec4 texel = texture(source, vec3(coordinate, layer));\n"
|
||||
" color = vec4(to_linear(texel.rgb), texel.a);\n"
|
||||
" if (vignette > 0.0)\n"
|
||||
" {\n"
|
||||
" vec2 corner = disc * 0.5 + 0.5;\n"
|
||||
" vec2 away = vec2(mix(tangents.x, tangents.y, corner.x), mix(tangents.z, tangents.w, corner.y));\n"
|
||||
" float edge = mix(1.8, 0.45, vignette);\n"
|
||||
" color.rgb *= 1.0 - smoothstep(edge - 0.3, edge, length(away));\n"
|
||||
" }\n"
|
||||
"}\n";
|
||||
|
||||
static GLuint compile_shader(GLenum type, const char *text)
|
||||
{
|
||||
GLuint shader = glCreateShader(type);
|
||||
@@ -293,6 +327,38 @@ static int copy_initialize(void)
|
||||
xr.cut_disc_uniform = glGetUniformLocation(xr.program, "cut_disc");
|
||||
xr.vignette_uniform = glGetUniformLocation(xr.program, "vignette");
|
||||
xr.tangents_uniform = glGetUniformLocation(xr.program, "tangents");
|
||||
xr.copy_array_layer = -1;
|
||||
{
|
||||
GLuint array_vertex = compile_shader(GL_VERTEX_SHADER, copy_vertex_shader);
|
||||
GLuint array_fragment = compile_shader(GL_FRAGMENT_SHADER, copy_array_fragment_shader);
|
||||
|
||||
if (array_vertex && array_fragment)
|
||||
{
|
||||
xr.array_program = glCreateProgram();
|
||||
glAttachShader(xr.array_program, array_vertex);
|
||||
glAttachShader(xr.array_program, array_fragment);
|
||||
glLinkProgram(xr.array_program);
|
||||
glGetProgramiv(xr.array_program, GL_LINK_STATUS, &status);
|
||||
if (!status)
|
||||
{
|
||||
host_logf(HOST_LOG_ERROR, "xr: array copy program does not link");
|
||||
glDeleteProgram(xr.array_program);
|
||||
xr.array_program = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
xr.array_source_uniform = glGetUniformLocation(xr.array_program, "source");
|
||||
xr.array_rect_uniform = glGetUniformLocation(xr.array_program, "rect");
|
||||
xr.array_layer_uniform = glGetUniformLocation(xr.array_program, "layer");
|
||||
xr.array_vignette_uniform = glGetUniformLocation(xr.array_program, "vignette");
|
||||
xr.array_tangents_uniform = glGetUniformLocation(xr.array_program, "tangents");
|
||||
}
|
||||
}
|
||||
if (array_vertex)
|
||||
glDeleteShader(array_vertex);
|
||||
if (array_fragment)
|
||||
glDeleteShader(array_fragment);
|
||||
}
|
||||
glGenVertexArrays(1, &xr.vertex_array);
|
||||
glGenSamplers(1, &xr.sampler);
|
||||
glSamplerParameteri(xr.sampler, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
@@ -1150,6 +1216,20 @@ int host_xr_copy_layer(int layer, uint32_t texture, int clear_alpha)
|
||||
return host_xr_copy_layer_region(layer, texture, clear_alpha, 0.0f, 0.0f, 1.0f, 1.0f);
|
||||
}
|
||||
|
||||
/* copies a layer of a texture array (the eyes drawn at once) into a
|
||||
layer's image, opaque */
|
||||
int host_xr_copy_layer_array(int layer, uint32_t texture, int array_layer)
|
||||
{
|
||||
int copied;
|
||||
|
||||
if (!xr.array_program)
|
||||
return 0;
|
||||
xr.copy_array_layer = array_layer;
|
||||
copied = host_xr_copy_layer_region(layer, texture, HALO_XR_COPY_OPAQUE, 0.0f, 0.0f, 1.0f, 1.0f);
|
||||
xr.copy_array_layer = -1;
|
||||
return copied;
|
||||
}
|
||||
|
||||
/* copies part of a render target (left, top, right, bottom, 0..1 from its
|
||||
top left) into a layer's image */
|
||||
int host_xr_copy_layer_region(int layer, uint32_t texture, int clear_alpha, float left, float top, float right,
|
||||
@@ -1184,7 +1264,38 @@ int host_xr_copy_layer_region(int layer, uint32_t texture, int clear_alpha, floa
|
||||
glDisable(GL_CULL_FACE);
|
||||
glDisable(GL_STENCIL_TEST);
|
||||
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
|
||||
if (texture)
|
||||
if (texture && xr.copy_array_layer >= 0)
|
||||
{
|
||||
/* a layer of the eyes' texture array (multiview) */
|
||||
glUseProgram(xr.array_program);
|
||||
glUniform1i(xr.array_source_uniform, 0);
|
||||
glUniform4f(xr.array_rect_uniform, left, top, right, bottom);
|
||||
glUniform1f(xr.array_layer_uniform, (float)xr.copy_array_layer);
|
||||
if ((layer == HALO_XR_LAYER_EYE_LEFT || layer == HALO_XR_LAYER_EYE_RIGHT) && xr.vignette > 0.001f)
|
||||
{
|
||||
const XrFovf *fov = &xr.views[layer - HALO_XR_LAYER_EYE_LEFT].fov;
|
||||
|
||||
glUniform1f(xr.array_vignette_uniform, xr.vignette);
|
||||
glUniform4f(xr.array_tangents_uniform, tanf(fov->angleLeft), tanf(fov->angleRight),
|
||||
tanf(fov->angleDown), tanf(fov->angleUp));
|
||||
}
|
||||
else
|
||||
{
|
||||
glUniform1f(xr.array_vignette_uniform, 0.0f);
|
||||
}
|
||||
glBindVertexArray(xr.vertex_array);
|
||||
glBindTexture(GL_TEXTURE_2D_ARRAY, texture);
|
||||
glBindSampler(0, xr.sampler);
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
glBindTexture(GL_TEXTURE_2D_ARRAY, 0);
|
||||
if (clear_alpha == HALO_XR_COPY_OPAQUE)
|
||||
{
|
||||
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE);
|
||||
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
}
|
||||
}
|
||||
else if (texture)
|
||||
{
|
||||
GLint swizzle_r, swizzle_b;
|
||||
|
||||
|
||||
@@ -57,6 +57,7 @@ host_xr_copy_layer_region
|
||||
host_xr_end_frame
|
||||
host_xr_haptic
|
||||
host_xr_set_vignette
|
||||
host_xr_copy_layer_array
|
||||
host_android_path
|
||||
|
||||
host_errno
|
||||
@@ -50,6 +50,20 @@ void halo_vr_eye_camera(int eye, const float game_position[3],
|
||||
/* copies the back buffer, just drawn for an eye, to that eye's image */
|
||||
void halo_vr_capture_eye(int eye);
|
||||
|
||||
/* Both eyes in one draw (vr.multiview, where GL_OVR_multiview2 is): whether
|
||||
this frame draws so; the camera between the eyes whose view takes in both
|
||||
(as halo_vr_eye_camera), which also starts drawing into both eyes' layers;
|
||||
and after the draw, the layers taken for the eyes' images. */
|
||||
int halo_vr_multiview(void);
|
||||
void halo_vr_center_camera(const float game_position[3], float position[3], float forward[3], float up[3],
|
||||
float *vertical_field_of_view, float frustum_bounds[4]);
|
||||
void halo_vr_capture_multiview(void);
|
||||
/* the sky, drawn about the camera, is being drawn: infinitely far */
|
||||
void halo_vr_multiview_sky(int sky);
|
||||
/* the shared camera's projection's [2][2] and [3][2], for what the game
|
||||
projects itself (sprites, lens flare tests) */
|
||||
void halo_vr_multiview_projection(float projection_22, float projection_32);
|
||||
|
||||
/* clears the back buffer to transparent black for the overlay, after the
|
||||
eyes; menus_active chooses where the overlay is shown */
|
||||
void halo_vr_begin_overlay(int menus_active);
|
||||
|
||||
+244
-10
@@ -175,8 +175,12 @@ struct vertex_shader_object
|
||||
struct vertex_element elements[XGPU_VERTEX_ATTRIBUTE_COUNT];
|
||||
unsigned long element_count;
|
||||
unsigned long packed_mask;
|
||||
/* [0] streams per the declaration, [1] immediate mode (all floats) */
|
||||
GLuint shader[2];
|
||||
/* [0] streams per the declaration, [1] immediate mode (all floats);
|
||||
[2] and [3] the same for both eyes at once (multiview) */
|
||||
GLuint shader[4];
|
||||
/* its position is a projected one (nv2a_vsh.c's clip capture): known,
|
||||
and so; and whether a multiview draw with it was looked at (diagnostics) */
|
||||
BOOL projection_known, projected, multiview_checked;
|
||||
};
|
||||
|
||||
/* ---------- programs */
|
||||
@@ -224,6 +228,13 @@ struct program_entry
|
||||
GLint bump_matrix, bump_luminance, texture_scale;
|
||||
GLint texture_lod_bias;
|
||||
GLint screen_offset;
|
||||
/* multiview: each view's correction (nv2a_vsh.c), and multiview.serial
|
||||
when it was last uploaded; the projection's depth, and whether the draw
|
||||
tests depth (what the uniform holds) */
|
||||
GLint mv_rows;
|
||||
unsigned long mv_serial;
|
||||
GLint mv_depth;
|
||||
float mv_depth_value[4];
|
||||
|
||||
/* the vertex constants c[0..constant_count) the program uses; with
|
||||
consecutive locations, a changed range is uploaded by itself */
|
||||
@@ -370,6 +381,8 @@ struct gl_device
|
||||
{
|
||||
unsigned short slot;
|
||||
unsigned short counter;
|
||||
/* drawn into both eyes at once: counted twice */
|
||||
unsigned short views;
|
||||
} frame_tests[STREAM_BUFFER_RING][VISIBILITY_FRAME_TESTS];
|
||||
unsigned long frame_test_count[STREAM_BUFFER_RING];
|
||||
GLuint visibility_latest[VISIBILITY_TEST_SLOTS];
|
||||
@@ -855,6 +868,162 @@ static GLuint framebuffer_get(GLuint color, GLuint depth)
|
||||
return entry->framebuffer;
|
||||
}
|
||||
|
||||
#ifdef HALO_ANDROID
|
||||
/* ---------- multiview (VR: both eyes in one draw, GL_OVR_multiview2)
|
||||
|
||||
Between d3d8_multiview_begin and _end, what the game draws into the back
|
||||
buffer goes into two layers at once, one per eye: every vertex program is
|
||||
compiled for two views (nv2a_vsh.c) and moves each view's positions to its
|
||||
eye (rows, from vr.c). The game draws the scene once, from between the eyes.
|
||||
A texture read of the back buffer meanwhile (a screen effect: the sun's
|
||||
glow measures the picture about the sun) gets the eyes' pictures, each laid
|
||||
where it lies in the shared camera's (resolve_rect), the left one over the
|
||||
right. */
|
||||
|
||||
static struct
|
||||
{
|
||||
BOOL active;
|
||||
/* the back buffer's target, which the layers stand in for */
|
||||
struct render_target_entry *back_buffer;
|
||||
/* drawn since the back buffer last got the left eye's picture */
|
||||
BOOL drawn;
|
||||
GLuint color, depth, framebuffer;
|
||||
unsigned long width, height;
|
||||
float rows[4][4];
|
||||
unsigned long serial;
|
||||
/* where each eye's picture lies in the shared camera's: left, top,
|
||||
right, bottom, 0..1 from its top left */
|
||||
float resolve_rect[2][4];
|
||||
/* the shared camera's projection's depth, as the game projects a point
|
||||
itself (rasterizer_widget_project_billboard): z = [0] + [1] / depth */
|
||||
float projection_depth[2];
|
||||
} multiview;
|
||||
|
||||
/* the bound targets are the layers */
|
||||
static BOOL targets_multiview;
|
||||
|
||||
static struct vertex_shader_object *current_program(void);
|
||||
|
||||
static GLuint framebuffer_get(GLuint color, GLuint depth);
|
||||
|
||||
/* the eyes' pictures into the back buffer's own texture, for a draw that
|
||||
reads it; the bindings it changes are put back */
|
||||
static void multiview_resolve(void)
|
||||
{
|
||||
static GLuint read_framebuffer;
|
||||
GLboolean scissor = glIsEnabled(GL_SCISSOR_TEST);
|
||||
int eye;
|
||||
|
||||
if (!multiview.drawn)
|
||||
return;
|
||||
multiview.drawn = FALSE;
|
||||
if (!read_framebuffer)
|
||||
glGenFramebuffers(1, &read_framebuffer);
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, read_framebuffer);
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffer_get(multiview.back_buffer->target.texture, 0));
|
||||
if (scissor)
|
||||
glDisable(GL_SCISSOR_TEST);
|
||||
for (eye = 1; eye >= 0; eye--)
|
||||
{
|
||||
const float *rect = multiview.resolve_rect[eye];
|
||||
|
||||
glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, multiview.color, 0, eye);
|
||||
/* (rows from the top in both: GL's y 0 is the picture's top) */
|
||||
glBlitFramebuffer(0, 0, (GLint)multiview.width, (GLint)multiview.height,
|
||||
(GLint)(rect[0] * multiview.width + 0.5f), (GLint)(rect[1] * multiview.height + 0.5f),
|
||||
(GLint)(rect[2] * multiview.width + 0.5f), (GLint)(rect[3] * multiview.height + 0.5f),
|
||||
GL_COLOR_BUFFER_BIT, GL_LINEAR);
|
||||
}
|
||||
if (scissor)
|
||||
glEnable(GL_SCISSOR_TEST);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, gl_state.framebuffer != (GLuint)-1 ? gl_state.framebuffer : 0);
|
||||
}
|
||||
|
||||
int d3d8_multiview_supported(void)
|
||||
{
|
||||
return device.gl_ready && xgpu_capabilities.multiview;
|
||||
}
|
||||
|
||||
/* rows: per view, the correction of x and of y (nv2a_vsh.c); resolve_rect:
|
||||
where each eye's picture lies in the shared camera's */
|
||||
int d3d8_multiview_begin(const float rows[4][4], const float resolve_rect[2][4])
|
||||
{
|
||||
struct render_target_entry *back_buffer;
|
||||
|
||||
if (!d3d8_multiview_supported() || !(back_buffer = render_target_get(&device.back_buffer)))
|
||||
return FALSE;
|
||||
if (!multiview.framebuffer || multiview.width != back_buffer->target.gl_width ||
|
||||
multiview.height != back_buffer->target.gl_height)
|
||||
{
|
||||
static const GLenum draw_buffer = GL_COLOR_ATTACHMENT0;
|
||||
GLenum status;
|
||||
|
||||
if (multiview.framebuffer)
|
||||
{
|
||||
glDeleteFramebuffers(1, &multiview.framebuffer);
|
||||
glDeleteTextures(1, &multiview.color);
|
||||
glDeleteTextures(1, &multiview.depth);
|
||||
}
|
||||
multiview.width = back_buffer->target.gl_width;
|
||||
multiview.height = back_buffer->target.gl_height;
|
||||
glGenTextures(1, &multiview.color);
|
||||
glBindTexture(GL_TEXTURE_2D_ARRAY, multiview.color);
|
||||
glTexStorage3D(GL_TEXTURE_2D_ARRAY, 1, GL_RGBA8, (GLsizei)multiview.width, (GLsizei)multiview.height, 2);
|
||||
glGenTextures(1, &multiview.depth);
|
||||
glBindTexture(GL_TEXTURE_2D_ARRAY, multiview.depth);
|
||||
glTexStorage3D(GL_TEXTURE_2D_ARRAY, 1, GL_DEPTH24_STENCIL8, (GLsizei)multiview.width,
|
||||
(GLsizei)multiview.height, 2);
|
||||
glBindTexture(GL_TEXTURE_2D_ARRAY, 0);
|
||||
glGenFramebuffers(1, &multiview.framebuffer);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, multiview.framebuffer);
|
||||
glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, multiview.color, 0, 0, 2);
|
||||
glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, multiview.depth, 0, 0, 2);
|
||||
glDrawBuffers(1, &draw_buffer);
|
||||
status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
|
||||
xgpu_gl_state_invalidate();
|
||||
platform_log("multiview: %lux%lu layers, framebuffer %s", multiview.width, multiview.height,
|
||||
status == GL_FRAMEBUFFER_COMPLETE ? "complete" : "INCOMPLETE");
|
||||
if (status != GL_FRAMEBUFFER_COMPLETE)
|
||||
{
|
||||
xgpu_capabilities.multiview = FALSE;
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
memcpy(multiview.rows, rows, sizeof(multiview.rows));
|
||||
memcpy(multiview.resolve_rect, resolve_rect, sizeof(multiview.resolve_rect));
|
||||
multiview.serial++;
|
||||
multiview.back_buffer = back_buffer;
|
||||
multiview.drawn = FALSE;
|
||||
multiview.active = TRUE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* the projection's depth for what the game projects itself: z = a + b /
|
||||
depth for a point that far along the view */
|
||||
void d3d8_multiview_depth(float a, float b)
|
||||
{
|
||||
multiview.projection_depth[0] = a;
|
||||
multiview.projection_depth[1] = b;
|
||||
}
|
||||
|
||||
/* new rows for the draws that follow (the sky's, vr.c) */
|
||||
void d3d8_multiview_rows(const float rows[4][4])
|
||||
{
|
||||
if (!multiview.active)
|
||||
return;
|
||||
memcpy(multiview.rows, rows, sizeof(multiview.rows));
|
||||
multiview.serial++;
|
||||
}
|
||||
|
||||
/* the eyes' layers (GL_TEXTURE_2D_ARRAY), after d3d8_multiview_end */
|
||||
unsigned int d3d8_multiview_end(void)
|
||||
{
|
||||
multiview.active = FALSE;
|
||||
targets_multiview = FALSE;
|
||||
return multiview.color;
|
||||
}
|
||||
|
||||
#endif
|
||||
/* the pixels per unit of the bound targets (render_target_get) */
|
||||
static float target_scale[2] = { 1.0f, 1.0f };
|
||||
|
||||
@@ -880,6 +1049,18 @@ static BOOL bind_targets(BOOL *has_depth)
|
||||
/* viewports and clears are in the targets' units (render_target_get) */
|
||||
target_scale[0] = color ? color->target.scale[0] : depth->target.scale[0];
|
||||
target_scale[1] = color ? color->target.scale[1] : depth->target.scale[1];
|
||||
#ifdef HALO_ANDROID
|
||||
/* VR: the back buffer, while both eyes are drawn at once, is their
|
||||
layers (d3d8_multiview_begin), with a depth buffer of their own */
|
||||
targets_multiview = multiview.active && color == multiview.back_buffer;
|
||||
if (targets_multiview)
|
||||
{
|
||||
multiview.drawn = TRUE;
|
||||
state_framebuffer(multiview.framebuffer);
|
||||
*has_depth = depth != NULL;
|
||||
return TRUE;
|
||||
}
|
||||
#endif
|
||||
state_framebuffer(framebuffer_get(color ? color->target.texture : 0, depth ? depth->target.texture : 0));
|
||||
*has_depth = depth != NULL;
|
||||
return TRUE;
|
||||
@@ -917,9 +1098,11 @@ static void gl_initialize(void)
|
||||
(host_gl_has_extension("GL_EXT_texture_compression_dxt1") &&
|
||||
host_gl_has_extension("GL_ANGLE_texture_compression_dxt3") &&
|
||||
host_gl_has_extension("GL_ANGLE_texture_compression_dxt5"));
|
||||
platform_log("OpenGL ES %d.%d: copy image %d, border clamp %d, anisotropy %d, S3TC %d, sample counting %d",
|
||||
(int)major, (int)minor, xgpu_capabilities.copy_image, xgpu_capabilities.border_clamp,
|
||||
xgpu_capabilities.anisotropy, xgpu_capabilities.s3tc, xgpu_capabilities.atomic_counters);
|
||||
xgpu_capabilities.multiview = host_gl_has_extension("GL_OVR_multiview2");
|
||||
platform_log("OpenGL ES %d.%d: copy image %d, border clamp %d, anisotropy %d, S3TC %d, sample counting %d, "
|
||||
"multiview %d", (int)major, (int)minor, xgpu_capabilities.copy_image, xgpu_capabilities.border_clamp,
|
||||
xgpu_capabilities.anisotropy, xgpu_capabilities.s3tc, xgpu_capabilities.atomic_counters,
|
||||
xgpu_capabilities.multiview);
|
||||
}
|
||||
#else
|
||||
if (config_boolean("debug.gl_debug"))
|
||||
@@ -1414,6 +1597,7 @@ HRESULT WINAPI D3DDevice_EndVisibilityTest(DWORD index)
|
||||
{
|
||||
device.frame_tests[device.buffer_ring][*count].slot = (unsigned short)index;
|
||||
device.frame_tests[device.buffer_ring][*count].counter = (unsigned short)device.counter_active;
|
||||
device.frame_tests[device.buffer_ring][*count].views = (unsigned short)(multiview.active ? 2 : 1);
|
||||
(*count)++;
|
||||
}
|
||||
return S_OK;
|
||||
@@ -1473,7 +1657,7 @@ static void visibility_results_update(void)
|
||||
{
|
||||
const struct visibility_frame_test *entry = &device.frame_tests[ring][test];
|
||||
|
||||
device.visibility_latest[entry->slot] = counts[entry->counter];
|
||||
device.visibility_latest[entry->slot] = counts[entry->counter] / (entry->views ? entry->views : 1);
|
||||
}
|
||||
}
|
||||
device.frame_test_count[ring] = 0;
|
||||
@@ -1855,16 +2039,21 @@ static unsigned long hash_words(const void *data, unsigned long size)
|
||||
return hash;
|
||||
}
|
||||
|
||||
static GLuint vertex_shader_get(struct vertex_shader_object *program, BOOL immediate)
|
||||
static GLuint vertex_shader_get(struct vertex_shader_object *program, BOOL immediate, BOOL multiview_view)
|
||||
{
|
||||
int variant = immediate ? 1 : 0;
|
||||
int variant = (immediate ? 1 : 0) + (multiview_view ? 2 : 0);
|
||||
|
||||
if (!program->shader[variant])
|
||||
{
|
||||
char *source = nv2a_vertex_shader_to_glsl(program->instructions, program->instruction_count,
|
||||
immediate ? 0 : device.vertex_shader->packed_mask);
|
||||
immediate ? 0 : device.vertex_shader->packed_mask, multiview_view);
|
||||
|
||||
program->shader[variant] = compile_shader(GL_VERTEX_SHADER, source, "vertex");
|
||||
if (!program->projection_known)
|
||||
{
|
||||
program->projected = strstr(source, "clip_captured = true") != NULL;
|
||||
program->projection_known = TRUE;
|
||||
}
|
||||
if (debug_settings.dump_shaders)
|
||||
{
|
||||
char path[512];
|
||||
@@ -2018,6 +2207,9 @@ static struct program_entry *program_get(GLuint vertex_shader, GLuint fragment_s
|
||||
entry->texture_scale = glGetUniformLocation(entry->program, "texture_scale");
|
||||
entry->texture_lod_bias = glGetUniformLocation(entry->program, "texture_lod_bias");
|
||||
entry->screen_offset = glGetUniformLocation(entry->program, "screen_offset");
|
||||
entry->mv_rows = glGetUniformLocation(entry->program, "mv_rows");
|
||||
entry->mv_depth = glGetUniformLocation(entry->program, "mv_depth");
|
||||
entry->mv_depth_value[0] = entry->mv_depth_value[1] = -1.0e30f;
|
||||
for (stage = 0; stage < D3DTSS_MAXSTAGES; stage++)
|
||||
{
|
||||
char name[8];
|
||||
@@ -2241,6 +2433,24 @@ static void bind_textures(struct nv2a_pixel_shader_key *key, float texture_scale
|
||||
|
||||
if (target)
|
||||
{
|
||||
#ifdef HALO_ANDROID
|
||||
if (multiview.active && multiview.back_buffer && target == &multiview.back_buffer->target)
|
||||
{
|
||||
static unsigned long logged[16];
|
||||
unsigned long id = current_program() ? current_program()->id : 0;
|
||||
int slot, known = FALSE;
|
||||
|
||||
/* (diagnostics: each vertex program that reads the picture) */
|
||||
for (slot = 0; slot < 16 && logged[slot]; slot++)
|
||||
known |= logged[slot] == id + 1;
|
||||
if (!known && slot < 16)
|
||||
{
|
||||
logged[slot] = id + 1;
|
||||
platform_log("multiview: vertex program %lu reads the picture", id);
|
||||
}
|
||||
multiview_resolve();
|
||||
}
|
||||
#endif
|
||||
xgpu_texture_describe(texture->Format, texture->Size, &description);
|
||||
gl_texture = target->texture;
|
||||
gl_target = GL_TEXTURE_2D;
|
||||
@@ -2563,7 +2773,7 @@ static struct program_entry *prepare_draw(BOOL immediate)
|
||||
key.count_samples = device.visibility_test_active && xgpu_capabilities.atomic_counters;
|
||||
#endif
|
||||
|
||||
entry = program_get(vertex_shader_get(program, immediate), fragment_shader_get(&key));
|
||||
entry = program_get(vertex_shader_get(program, immediate, targets_multiview), fragment_shader_get(&key));
|
||||
if (!entry)
|
||||
{
|
||||
stats.skipped_link++;
|
||||
@@ -2577,6 +2787,30 @@ static struct program_entry *prepare_draw(BOOL immediate)
|
||||
stats.draws++;
|
||||
state_program(entry->program);
|
||||
#ifdef HALO_ANDROID
|
||||
if (targets_multiview && entry->mv_rows >= 0 && entry->mv_serial != multiview.serial)
|
||||
{
|
||||
glUniform4fv(entry->mv_rows, 4, &multiview.rows[0][0]);
|
||||
entry->mv_serial = multiview.serial;
|
||||
}
|
||||
if (targets_multiview && !program->multiview_checked)
|
||||
{
|
||||
/* (diagnostics: a vertex program whose position is not a projected
|
||||
one, drawn depth tested into both eyes, is something the game
|
||||
placed in screen space itself, which multiview puts at infinity) */
|
||||
program->multiview_checked = TRUE;
|
||||
if (!program->projected && has_depth && D3D__RenderState[D3DRS_ZENABLE])
|
||||
platform_log("multiview: vertex program %lu draws in screen space, depth tested", program->id);
|
||||
}
|
||||
if (targets_multiview && entry->mv_depth >= 0)
|
||||
{
|
||||
float depth[4];
|
||||
|
||||
depth[0] = multiview.projection_depth[0];
|
||||
depth[1] = multiview.projection_depth[1];
|
||||
depth[2] = has_depth && D3D__RenderState[D3DRS_ZENABLE] ? 1.0f : 0.0f;
|
||||
depth[3] = 0.0f;
|
||||
uniform_vec4(entry->mv_depth, entry->mv_depth_value, depth, 1);
|
||||
}
|
||||
if (key.count_samples)
|
||||
glBindBufferRange(GL_ATOMIC_COUNTER_BUFFER, 0, device.visibility_counters,
|
||||
(GLintptr)(device.counter_active * sizeof(GLuint)), sizeof(GLuint));
|
||||
|
||||
@@ -128,6 +128,10 @@ this list to generate the guest's entry points */
|
||||
X(glGenQueries) \
|
||||
X(glBeginQuery) \
|
||||
X(glEndQuery) \
|
||||
X(glTexStorage3D) \
|
||||
X(glFramebufferTextureLayer) \
|
||||
X(glFramebufferTextureMultiviewOVR) \
|
||||
X(glIsEnabled) \
|
||||
X(glGetQueryObjectuiv)
|
||||
/* ANDROID_GL_FUNCTIONS_END */
|
||||
#else
|
||||
@@ -343,6 +347,10 @@ pointers, sees the declarations without these aliases */
|
||||
#define glGenQueries halo_glGenQueries
|
||||
#define glBeginQuery halo_glBeginQuery
|
||||
#define glEndQuery halo_glEndQuery
|
||||
#define glTexStorage3D halo_glTexStorage3D
|
||||
#define glFramebufferTextureLayer halo_glFramebufferTextureLayer
|
||||
#define glFramebufferTextureMultiviewOVR halo_glFramebufferTextureMultiviewOVR
|
||||
#define glIsEnabled halo_glIsEnabled
|
||||
#define glGetQueryObjectuiv halo_glGetQueryObjectuiv
|
||||
#else
|
||||
#define glGetString halo_glGetString
|
||||
|
||||
@@ -195,13 +195,30 @@ static const char shader_prologue[] =
|
||||
"}\n";
|
||||
|
||||
char *nv2a_vertex_shader_to_glsl(const DWORD *instructions, unsigned long instruction_count,
|
||||
unsigned long packed_attribute_mask)
|
||||
unsigned long packed_attribute_mask, int multiview)
|
||||
{
|
||||
struct xgpu_text text = { 0 };
|
||||
unsigned long index;
|
||||
|
||||
#ifdef HALO_ANDROID
|
||||
xgpu_text_append(&text, "#version %s\n", xgpu_capabilities.shading_language);
|
||||
if (multiview)
|
||||
{
|
||||
/* VR: one draw into both eyes' layers. The scene is posed for a
|
||||
camera between the eyes whose view covers both; each view moves the
|
||||
position to its eye (half the eyes' distance aside) and onto its
|
||||
own view's edges. As x' = a x + b w + c and y' likewise, with w the
|
||||
depth along the view: mv_rows[2 * view] and [2 * view + 1];
|
||||
what is drawn in screen space goes without c, as though far. */
|
||||
xgpu_text_append(&text,
|
||||
"#extension GL_OVR_multiview2 : require\n"
|
||||
"layout(num_views = 2) in;\n"
|
||||
"uniform vec4 mv_rows[4];\n"
|
||||
/* the projection's depth: z = x + y / depth; z: depth tested */
|
||||
"uniform vec4 mv_depth;\n");
|
||||
}
|
||||
#else
|
||||
(void)multiview;
|
||||
#endif
|
||||
xgpu_text_append(&text, "%s", shader_prologue);
|
||||
for (index = 0; index < XGPU_VERTEX_ATTRIBUTE_COUNT; index++)
|
||||
@@ -360,6 +377,7 @@ char *nv2a_vertex_shader_to_glsl(const DWORD *instructions, unsigned long instru
|
||||
GL: rows from the top, depth 0..1 */
|
||||
"\tgl_Position.y = -gl_Position.y;\n"
|
||||
"\tgl_Position.z = 2.0 * gl_Position.z - gl_Position.w;\n"
|
||||
"%s"
|
||||
#endif
|
||||
"\tgl_PointSize = oPts.x;\n"
|
||||
"\txD0 = clamp(oD0, 0.0, 1.0);\n"
|
||||
@@ -373,7 +391,28 @@ char *nv2a_vertex_shader_to_glsl(const DWORD *instructions, unsigned long instru
|
||||
"\txFog = oFog.x;\n"
|
||||
"}\n"
|
||||
#ifdef HALO_ANDROID
|
||||
, XGPU_VERTEX_CONSTANT_BIAS - 38, XGPU_VERTEX_CONSTANT_BIAS - 37
|
||||
, XGPU_VERTEX_CONSTANT_BIAS - 38, XGPU_VERTEX_CONSTANT_BIAS - 37,
|
||||
/* (what is drawn in screen space, which kept no clip position, is
|
||||
taken to each view as though infinitely far: a lens flare's sun
|
||||
lands where the sky's does) */
|
||||
/* (what the game projects itself, a sprite or a lens flare's
|
||||
test, comes as a screen position with w 1 and its depth in z: its
|
||||
eye's offset is by that depth, where it is depth tested) */
|
||||
multiview ?
|
||||
"\t{\n"
|
||||
"\t\tint row = int(gl_ViewID_OVR) * 2;\n"
|
||||
"\t\tfloat near = 0.0;\n"
|
||||
"\t\tif (clip_captured && abs(clip_position.w - 1.0) > 1.0e-5)\n"
|
||||
"\t\t\tnear = 1.0;\n"
|
||||
"\t\telse if (clip_captured && mv_depth.z > 0.5)\n"
|
||||
"\t\t{\n"
|
||||
"\t\t\tfloat depth = mv_depth.y / (clip_position.z - mv_depth.x);\n"
|
||||
"\t\t\tnear = depth > 0.0 ? 1.0 / depth : 0.0;\n"
|
||||
"\t\t}\n"
|
||||
"\t\tgl_Position.xy = vec2(dot(vec3(gl_Position.xw, near), mv_rows[row].xyz),\n"
|
||||
"\t\t\tdot(vec3(gl_Position.yw, near), mv_rows[row + 1].xyz));\n"
|
||||
"\t}\n" :
|
||||
""
|
||||
#endif
|
||||
);
|
||||
return text.buffer;
|
||||
|
||||
@@ -97,6 +97,9 @@ static const struct config_setting config_settings[] =
|
||||
"away from the player's body (walls are not checked)." },
|
||||
{ "vr.crouch_limit", _config_real, "0.9", "HALO_VR_CROUCH_LIMIT", _environment_value, _platform_android,
|
||||
"How far, in metres, the view follows your head down." },
|
||||
{ "vr.multiview", _config_boolean, "true", "HALO_VR_MULTIVIEW", _environment_value, _platform_android,
|
||||
"Draw both eyes in one pass (GL_OVR_multiview2), from between them: about\n"
|
||||
"half the drawing's cost on the processor. false: one eye after the other." },
|
||||
{ "vr.log_performance", _config_boolean, "false", "HALO_VR_LOG_PERFORMANCE", _environment_value, _platform_android,
|
||||
"Log the frame rate and where the frame time goes every five seconds\n"
|
||||
"(adb logcat -s halo)." },
|
||||
|
||||
@@ -42,6 +42,10 @@ static struct
|
||||
struct halo_xr_info info;
|
||||
struct halo_xr_frame frame;
|
||||
int frame_begun;
|
||||
/* the hands as last seen with the app in focus (the system menu over
|
||||
it reports none): held still meanwhile */
|
||||
struct halo_xr_hand focused_hands[HALO_XR_HAND_COUNT];
|
||||
int focused_hands_valid;
|
||||
int eyes_captured; /* bit per eye, this frame */
|
||||
int overlay_mode; /* HALO_XR_OVERLAY_*, this frame */
|
||||
int overlay_blend;
|
||||
@@ -91,6 +95,9 @@ static struct
|
||||
float seat_frame_yaw;
|
||||
int seat_frame_valid;
|
||||
float seat_turn;
|
||||
int multiview; /* vr.multiview: both eyes in one draw */
|
||||
int multiview_logged;
|
||||
int multiview_logged_views;
|
||||
int vehicle_first_person;
|
||||
int turret_handles; /* a gun's barrel rises as its handles go down */
|
||||
float gunner_camera_back; /* metres */
|
||||
@@ -201,6 +208,11 @@ static struct
|
||||
|
||||
/* d3d8_gl.c */
|
||||
unsigned int d3d8_back_buffer_texture(void);
|
||||
int d3d8_multiview_supported(void);
|
||||
int d3d8_multiview_begin(const float rows[4][4], const float resolve_rect[2][4]);
|
||||
void d3d8_multiview_rows(const float rows[4][4]);
|
||||
void d3d8_multiview_depth(float a, float b);
|
||||
unsigned int d3d8_multiview_end(void);
|
||||
void d3d8_clear_back_buffer(float red, float green, float blue, float alpha);
|
||||
void d3d8_clear_back_buffer_region(float left, float top, float right, float bottom);
|
||||
|
||||
@@ -423,6 +435,7 @@ void halo_vr_initialize(void)
|
||||
vr.two_hand_reach = (float)config_real("vr.two_hand_reach");
|
||||
vr.melee_swing_speed = (float)config_real("vr.melee_swing_speed");
|
||||
vr.grenade_throw_speed = (float)config_real("vr.grenade_throw_speed");
|
||||
vr.multiview = config_boolean("vr.multiview");
|
||||
vr.vehicle_first_person = config_boolean("vr.vehicle_first_person");
|
||||
vr.vehicle_spawn = config_boolean("debug.vehicle_spawn");
|
||||
vr.turret_handles = config_boolean("vr.turret_handles");
|
||||
@@ -982,6 +995,25 @@ void halo_vr_frame_begin(void)
|
||||
waited = now_us();
|
||||
if (!host_xr_begin_frame(&vr.frame))
|
||||
memset(&vr.frame, 0, sizeof(vr.frame));
|
||||
if (vr.frame.focused)
|
||||
{
|
||||
memcpy(vr.focused_hands, vr.frame.hands, sizeof(vr.focused_hands));
|
||||
vr.focused_hands_valid = 1;
|
||||
}
|
||||
else if (vr.focused_hands_valid && vr.frame.session_running)
|
||||
{
|
||||
/* the system menu is up: the hands stay where they were, their
|
||||
buttons let go (the weapon does not fall back to the head) */
|
||||
int hand;
|
||||
|
||||
memcpy(vr.frame.hands, vr.focused_hands, sizeof(vr.frame.hands));
|
||||
for (hand = 0; hand < HALO_XR_HAND_COUNT; hand++)
|
||||
{
|
||||
vr.frame.hands[hand].trigger = vr.frame.hands[hand].squeeze = 0.0f;
|
||||
vr.frame.hands[hand].thumbstick[0] = vr.frame.hands[hand].thumbstick[1] = 0.0f;
|
||||
vr.frame.hands[hand].buttons = 0;
|
||||
}
|
||||
}
|
||||
vr.frame_start_us = now_us();
|
||||
vr.wait_us += vr.frame_start_us - waited;
|
||||
vr.frame_begun = 1;
|
||||
@@ -1125,6 +1157,164 @@ void halo_vr_eye_camera(int eye, const float game_position[3],
|
||||
frustum_bounds[3] = view->tangent_up / half_tangent;
|
||||
}
|
||||
|
||||
/* ---------- both eyes at once (vr.multiview)
|
||||
|
||||
The scene is drawn once, from a camera between the eyes whose view takes in
|
||||
both eyes' (with a margin, for what is near: the eyes stand apart), into two
|
||||
layers; each layer's positions are moved from that camera's picture to its
|
||||
eye's (d3d8_multiview_begin, nv2a_vsh.c). With d the depth along the view,
|
||||
a point's picture across, x, is (2 tx - (L + R)) / (R - L) times d for the
|
||||
tangent tx = X / d of the view's edges L, R; up, y, likewise, but for rows
|
||||
from the top. An eye that stands t aside sees X - t: x' = a x + b d + c. */
|
||||
|
||||
#define MULTIVIEW_MARGIN 0.1f
|
||||
|
||||
static float multiview_bounds[4]; /* the shared view's L, R, D, U */
|
||||
static int multiview_drawing;
|
||||
/* the rows for what is drawn in the world, and for the sky, which is drawn
|
||||
about the camera, as though infinitely far: no eye stands aside of it */
|
||||
static float multiview_rows[2][4][4];
|
||||
|
||||
int halo_vr_multiview(void)
|
||||
{
|
||||
int available = vr.active && vr.multiview && d3d8_multiview_supported();
|
||||
|
||||
if (vr.active && vr.multiview && !vr.multiview_logged)
|
||||
{
|
||||
platform_log("VR: both eyes %s", available ? "in one draw (multiview)" : "drawn one by one (no multiview)");
|
||||
vr.multiview_logged = 1;
|
||||
}
|
||||
return available && halo_vr_stereo_frame();
|
||||
}
|
||||
|
||||
void halo_vr_center_camera(const float game_position[3], float position[3], float forward[3], float up[3],
|
||||
float *vertical_field_of_view, float frustum_bounds[4])
|
||||
{
|
||||
static const float local_forward[3] = { 0.0f, 0.0f, -1.0f };
|
||||
static const float local_up[3] = { 0.0f, 1.0f, 0.0f };
|
||||
static const float local_right[3] = { 1.0f, 0.0f, 0.0f };
|
||||
const struct halo_xr_view *views = vr.frame.views;
|
||||
float centre[3], offset[3], direction[3], right_local[3], up_local[3], rows[4][4];
|
||||
float left, right, down, top, half_tangent;
|
||||
int axis, eye;
|
||||
|
||||
vr.eyes_start_us = now_us();
|
||||
for (axis = 0; axis < 3; axis++)
|
||||
centre[axis] = (views[0].pose.position[axis] + views[1].pose.position[axis]) * 0.5f;
|
||||
tracked_offset(centre, offset);
|
||||
memcpy(vr.game_camera, game_position, sizeof(vr.game_camera));
|
||||
for (axis = 0; axis < 3; axis++)
|
||||
position[axis] = game_position[axis] + offset[axis] / METRES_PER_WORLD_UNIT;
|
||||
/* the eyes look alike (parallel views): the left one's way */
|
||||
quaternion_rotate(views[0].pose.orientation, local_forward, direction);
|
||||
local_to_game(direction, vr.body_yaw, forward);
|
||||
quaternion_rotate(views[0].pose.orientation, local_up, direction);
|
||||
local_to_game(direction, vr.body_yaw, up);
|
||||
quaternion_rotate(views[0].pose.orientation, local_right, right_local);
|
||||
quaternion_rotate(views[0].pose.orientation, local_up, up_local);
|
||||
|
||||
left = fminf(views[0].tangent_left, views[1].tangent_left) - MULTIVIEW_MARGIN;
|
||||
right = fmaxf(views[0].tangent_right, views[1].tangent_right) + MULTIVIEW_MARGIN;
|
||||
down = fminf(views[0].tangent_down, views[1].tangent_down) - MULTIVIEW_MARGIN;
|
||||
top = fmaxf(views[0].tangent_up, views[1].tangent_up) + MULTIVIEW_MARGIN;
|
||||
multiview_bounds[0] = left;
|
||||
multiview_bounds[1] = right;
|
||||
multiview_bounds[2] = down;
|
||||
multiview_bounds[3] = top;
|
||||
half_tangent = (top - down) * 0.5f;
|
||||
*vertical_field_of_view = 2.0f * atanf(half_tangent);
|
||||
frustum_bounds[0] = left / (half_tangent * (4.0f / 3.0f));
|
||||
frustum_bounds[1] = right / (half_tangent * (4.0f / 3.0f));
|
||||
frustum_bounds[2] = down / half_tangent;
|
||||
frustum_bounds[3] = top / half_tangent;
|
||||
|
||||
for (eye = 0; eye < HALO_XR_EYE_COUNT; eye++)
|
||||
{
|
||||
const struct halo_xr_view *view = &views[eye];
|
||||
float aside[3], tx, ty;
|
||||
float eye_width = view->tangent_right - view->tangent_left;
|
||||
float eye_height = view->tangent_up - view->tangent_down;
|
||||
|
||||
/* the eye's place from the shared camera, in its view's axes (world
|
||||
units) */
|
||||
for (axis = 0; axis < 3; axis++)
|
||||
aside[axis] = view->pose.position[axis] - centre[axis];
|
||||
tx = (aside[0] * right_local[0] + aside[1] * right_local[1] + aside[2] * right_local[2]) /
|
||||
METRES_PER_WORLD_UNIT;
|
||||
ty = (aside[0] * up_local[0] + aside[1] * up_local[1] + aside[2] * up_local[2]) / METRES_PER_WORLD_UNIT;
|
||||
rows[eye * 2][0] = (right - left) / eye_width;
|
||||
rows[eye * 2][1] = (left + right - view->tangent_left - view->tangent_right) / eye_width;
|
||||
rows[eye * 2][2] = -2.0f * tx / eye_width;
|
||||
rows[eye * 2][3] = 0.0f;
|
||||
/* (rows from the top: y runs against the tangents) */
|
||||
rows[eye * 2 + 1][0] = (top - down) / eye_height;
|
||||
rows[eye * 2 + 1][1] = -(down + top - view->tangent_down - view->tangent_up) / eye_height;
|
||||
rows[eye * 2 + 1][2] = 2.0f * ty / eye_height;
|
||||
rows[eye * 2 + 1][3] = 0.0f;
|
||||
}
|
||||
memcpy(multiview_rows[0], rows, sizeof(rows));
|
||||
memcpy(multiview_rows[1], rows, sizeof(rows));
|
||||
for (eye = 0; eye < HALO_XR_EYE_COUNT; eye++)
|
||||
multiview_rows[1][eye * 2][2] = multiview_rows[1][eye * 2 + 1][2] = 0.0f;
|
||||
if (!vr.multiview_logged_views)
|
||||
{
|
||||
/* (the correction assumes parallel views: say if they are not) */
|
||||
float other[3], apart[3];
|
||||
|
||||
quaternion_rotate(views[1].pose.orientation, local_forward, other);
|
||||
quaternion_rotate(views[0].pose.orientation, local_forward, direction);
|
||||
for (axis = 0; axis < 3; axis++)
|
||||
apart[axis] = views[1].pose.position[axis] - views[0].pose.position[axis];
|
||||
platform_log("VR multiview: views %.2f degrees apart, eyes %.1f mm apart; shared view %.2f..%.2f, %.2f..%.2f",
|
||||
acosf(fminf(1.0f, direction[0] * other[0] + direction[1] * other[1] + direction[2] * other[2])) * 180.0f / PI,
|
||||
sqrtf(apart[0] * apart[0] + apart[1] * apart[1] + apart[2] * apart[2]) * 1000.0f, left, right, down, top);
|
||||
vr.multiview_logged_views = 1;
|
||||
}
|
||||
{
|
||||
/* each eye's picture's place in the shared one (rows from the top) */
|
||||
float resolve_rect[2][4];
|
||||
|
||||
for (eye = 0; eye < HALO_XR_EYE_COUNT; eye++)
|
||||
{
|
||||
resolve_rect[eye][0] = (views[eye].tangent_left - left) / (right - left);
|
||||
resolve_rect[eye][1] = (top - views[eye].tangent_up) / (top - down);
|
||||
resolve_rect[eye][2] = (views[eye].tangent_right - left) / (right - left);
|
||||
resolve_rect[eye][3] = (top - views[eye].tangent_down) / (top - down);
|
||||
}
|
||||
multiview_drawing = d3d8_multiview_begin(rows, resolve_rect);
|
||||
}
|
||||
}
|
||||
|
||||
/* the shared camera's projection's depth (render.c: its [2][2] and [3][2]):
|
||||
what the game projects itself carries z = -[2][2] + [3][2] / depth */
|
||||
void halo_vr_multiview_projection(float projection_22, float projection_32)
|
||||
{
|
||||
d3d8_multiview_depth(-projection_22, projection_32);
|
||||
}
|
||||
|
||||
/* the sky is being drawn (render.c): as though infinitely far */
|
||||
void halo_vr_multiview_sky(int sky)
|
||||
{
|
||||
if (multiview_drawing)
|
||||
d3d8_multiview_rows(multiview_rows[sky ? 1 : 0]);
|
||||
}
|
||||
|
||||
/* after the shared draw: its layers become the eyes' images */
|
||||
void halo_vr_capture_multiview(void)
|
||||
{
|
||||
unsigned int layers = d3d8_multiview_end();
|
||||
|
||||
if (!multiview_drawing)
|
||||
return;
|
||||
multiview_drawing = 0;
|
||||
if (host_xr_copy_layer_array(HALO_XR_LAYER_EYE_LEFT, layers, 0))
|
||||
vr.eyes_captured |= 1;
|
||||
if (host_xr_copy_layer_array(HALO_XR_LAYER_EYE_RIGHT, layers, 1))
|
||||
vr.eyes_captured |= 2;
|
||||
if (vr.eyes_start_us)
|
||||
vr.eyes_us += now_us() - vr.eyes_start_us;
|
||||
}
|
||||
|
||||
void halo_vr_capture_eye(int eye)
|
||||
{
|
||||
if (!vr.active || eye < 0 || eye >= HALO_XR_EYE_COUNT)
|
||||
@@ -1872,6 +2062,18 @@ void halo_vr_eye_camera(int eye, const float game_position[3], float position[3]
|
||||
(void)vertical_field_of_view; (void)frustum_bounds;
|
||||
}
|
||||
void halo_vr_capture_eye(int eye) { (void)eye; }
|
||||
int halo_vr_multiview(void) { return 0; }
|
||||
void halo_vr_center_camera(const float game_position[3], float position[3], float forward[3], float up[3],
|
||||
float *vertical_field_of_view, float frustum_bounds[4])
|
||||
{
|
||||
(void)game_position; (void)position; (void)forward; (void)up; (void)vertical_field_of_view; (void)frustum_bounds;
|
||||
}
|
||||
void halo_vr_capture_multiview(void) {}
|
||||
void halo_vr_multiview_sky(int sky) { (void)sky; }
|
||||
void halo_vr_multiview_projection(float projection_22, float projection_32)
|
||||
{
|
||||
(void)projection_22; (void)projection_32;
|
||||
}
|
||||
void halo_vr_begin_overlay(int menus_active) { (void)menus_active; }
|
||||
int halo_vr_aim(short local_player_index, float current_yaw, float current_pitch, float *yaw, float *pitch)
|
||||
{
|
||||
|
||||
@@ -27,6 +27,8 @@ struct xgpu_capabilities
|
||||
BOOL atomic_counters;
|
||||
/* "300 es" or "310 es" */
|
||||
const char *shading_language;
|
||||
/* GL_OVR_multiview2: both eyes in one draw (VR, d3d8_multiview_begin) */
|
||||
BOOL multiview;
|
||||
};
|
||||
|
||||
extern struct xgpu_capabilities xgpu_capabilities;
|
||||
@@ -73,8 +75,10 @@ void xgpu_text_append(struct xgpu_text *text, const char *format, ...) __attribu
|
||||
header). Attributes whose bit is set in packed_attribute_mask are fed as
|
||||
NORMPACKED3 32-bit integers and unpacked in the shader. Returns a malloc'd
|
||||
string. */
|
||||
/* multiview: for a two-view (GL_OVR_multiview2) target, each view's
|
||||
position corrected from the shared one by mv_rows (d3d8_gl.c, VR) */
|
||||
char *nv2a_vertex_shader_to_glsl(const DWORD *instructions, unsigned long instruction_count,
|
||||
unsigned long packed_attribute_mask);
|
||||
unsigned long packed_attribute_mask, int multiview);
|
||||
|
||||
/* ---------- pixel shaders */
|
||||
|
||||
|
||||
@@ -775,11 +775,13 @@ static void handle_one_player_input(
|
||||
#endif
|
||||
#ifdef HALO_ANDROID
|
||||
{
|
||||
/* VR: the player faces where the head does (vr.c) */
|
||||
/* VR: the player faces where the head does (vr.c); not in a
|
||||
cinematic, whose camera the game has (seated, the controller
|
||||
would turn it on the screen) */
|
||||
real vr_yaw;
|
||||
real vr_pitch;
|
||||
|
||||
if (halo_vr_aim(
|
||||
if (!cinematic_in_progress() && halo_vr_aim(
|
||||
local_player_index,
|
||||
player->desired_angles.yaw,
|
||||
player->desired_angles.pitch,
|
||||
|
||||
+36
-1
@@ -389,7 +389,13 @@ static void render_window(
|
||||
if (!bink_playback_in_progress())
|
||||
{
|
||||
build_sprite_prepare_for_window();
|
||||
#ifdef HALO_ANDROID
|
||||
halo_vr_multiview_sky(TRUE);
|
||||
#endif
|
||||
render_sky();
|
||||
#ifdef HALO_ANDROID
|
||||
halo_vr_multiview_sky(FALSE);
|
||||
#endif
|
||||
first_person_weapon_render_update();
|
||||
lights_preprocess_scene();
|
||||
render_objects();
|
||||
@@ -700,7 +706,36 @@ static void render_vr_player_frame(
|
||||
if (nodes)
|
||||
halo_vr_set_seat_frame(window->local_player_index, (float)atan2(nodes[0].forward.j, nodes[0].forward.i));
|
||||
}
|
||||
for (eye = 0; eye < 2; eye++)
|
||||
if (halo_vr_multiview())
|
||||
{
|
||||
/* both eyes at once, from between them */
|
||||
struct render_window eye_window = *window;
|
||||
float position[3], forward[3], up[3], bounds[4];
|
||||
float vertical_field_of_view;
|
||||
real_rectangle2d frustum_bounds;
|
||||
|
||||
halo_vr_center_camera(game_position, position, forward, up, &vertical_field_of_view, bounds);
|
||||
render_vr_camera_set(&eye_window.render_camera, position, forward, up, vertical_field_of_view);
|
||||
render_vr_camera_set(&eye_window.rasterizer_camera, position, forward, up, vertical_field_of_view);
|
||||
frustum_bounds.x0 = bounds[0];
|
||||
frustum_bounds.x1 = bounds[1];
|
||||
frustum_bounds.y0 = bounds[2];
|
||||
frustum_bounds.y1 = bounds[3];
|
||||
{
|
||||
/* the depth of what the game projects itself (its sprites) */
|
||||
struct render_frustum frustum;
|
||||
|
||||
render_camera_build_frustum(&eye_window.rasterizer_camera, &frustum_bounds, &frustum, TRUE);
|
||||
halo_vr_multiview_projection(frustum.projection_matrix[2][2], frustum.projection_matrix[3][2]);
|
||||
}
|
||||
render_vr_eye_pass = TRUE;
|
||||
render_vr_frustum_bounds = &frustum_bounds;
|
||||
render_player_frame(&eye_window, NULL);
|
||||
render_vr_frustum_bounds = NULL;
|
||||
render_vr_eye_pass = FALSE;
|
||||
halo_vr_capture_multiview();
|
||||
}
|
||||
else for (eye = 0; eye < 2; eye++)
|
||||
{
|
||||
struct render_window eye_window = *window;
|
||||
float position[3], forward[3], up[3], bounds[4];
|
||||
|
||||
Reference in new issue
Block a user