Add entrypoints, headset backend code, fill in the Activity, and
add various special cases to account for the asynchronous render loop,
lack of sRGB support, and OpenGL state resets.
lovr.log is a new callback that is invoked whenever LÖVR wants to
send the project a message. For example, this could be a performance
warning from the graphics module, an error message from one of the
headset backends, or an API deprecation notice.
The callback's signature is (message, level, tag). The message is a
string containing the message to log, level is a string that is currently
one of "debug", "info", "warn", "error", and tag is an optional string
that is used to indicate the source of the message for grouping purposes.
The default implementation of the callback just prints the message,
but the callback can be overridden to do things like filter messages,
write them to a file, or even render them in VR. Projects can also
invoke the callback directly to log their own messages.
Tightness parameter is amount of force is exerted on collider to resolve
collisions and enforce joint operation. Low values make joints loose,
high values make it tight and can cause collider to overshot the joint
target. With tightness set to 0 the joint loses its function. Going
above 1 puts even more energy into joint oscillations. Tightness
parameter is called ERP in ODE manual.
The responseTime affects the time constant of physics simulation, both
for collisions and for joint inertia. Low responseTime values make
simulation tight and fast, higher values make it sluggish. For
collisions it affects how fast penetration is resolved, with higher
values resulting in spongy objects with more surface penetration and
slower collision resolving. For joints the responseTime is similar to
inertia, with higher responseTime values resulting in slow oscillations.
The oscillation frequency is also affected by collider mass, so
responseTime can be used to tweak the joint to get desired frequency
with specific collider mass. Values higher than 1 are often desirable,
especially for very light objects. Unlike tightness, responseTime is
tweaked in orders of magnitude with useful values (depending on mass)
being between 10^-8 and 10^8.
Both parameters can be applied to World for simulation-wide usage, or
specified per-joint in case of distance and ball joints. Other joints
don't allow customizing these parameters, and will use World settings
instead..
When creating shapes and joints, arguments accept either coordinate
numbers as before, or vec3 objects. For functions that receive more than
one set of coordinates, any combination of coordinates and vectors is
accepted.
There are 4 new devices: beacon/1 through beacon/4. They represent
tracking reference like StemaVR base stations or Oculus cameras.
There are 4 because that's how many base stations you can have in
a single tracking setup.
Right now only OpenVR exposes poses for them.
When creating colliders, setting gravity or casting rays on world,
arguments accept either coordinate numbers as before, or vec3 objects.
For functions that receive more than one set of coordinates, any
combination of coordinates and vectors is accepted.
Based on Slack conversation, the following changes:
- lovr.event.quit("restart") no longer supported
- lovr.event.quit no longer takes restart "cookie"
- When lovr.event.restart() called, lovr.quit() is not called, instead lovr.restart() is called
- Value returned from lovr.restart(), when called, becomes the cookie
- lovr.event.quit takes the lovr.event.quit() return code as an argument
lovr.run() is unchanged, it still returns (exit code | "restart", cookie).
- There is now just one "playing" state.
- Instead of rewind, use :seek(0).
Note that now there is no way to resume or rewind all tracked sources.
This can be improved in the future if there's a need for it, probably
using variadic or table-based variants of the audio module functions.
Lua was happily compiling nil chunks and making them return empty
strings, which was not a good error experience in situations where
your file couldn't be loaded properly. Now we return nil plus an
error message, which matches LOVE and other Lua conventions.
Unlike lovr.timer.getTime, this is the predicted time at which the
current frame will be displayed. It can be used in place of
lovr.timer.getTime for smoother animations. It's unclear if this
could be used for a suitable replacement for dt though.
Currently nobody returns data for them, though headset drivers could
start to provide poses estimated from the head pose and IPD info.
This also makes it easier to integrate eye tracking later.
The previous implementation relied on glShaderSource inferring source
lengths when the lengths weren't specified. This relies on the sources
being properly null-terminated, however, which isn't the case due to
file loading changes which now use pointer + length. This could cause
intermittent crashes.
Changing this on the shader side meant adding some extra arguments for
passing around shader source lengths. For most of the other cases, where
we're using string literals as the sources, we can just specify -1 as
the length, since OpenGL will calculate the string length for you any
time the length is negative.
conf doesn't work in threads. While this seems problematic, it can
kinda be considered the right thing. Right now the math module can't
be required at all in threads which seems bad, so we'll make it work
and just ignore the "globals" flag for now.
Instead of having a bogus samples ivar in AudioStream when it's raw, use it to denote the amount of audio data enqueued.
This means `stream:getDuration()` gets the same meaning as `stream:getQueueLength()` (except in seconds instead of samples), so we can remove the latter.
Channels need to be removed from the global array/map when destroyed.
Note that this exposes an infinite loop in map_remove, which will
be fixed later.
Note also that Channel's are retained if they have any messages in
them, to prevent releasing a channel while it has pending messages.
- lovr.graphics.tock returns the latest value of the timer, or 0.
- Timers are not in the stats table anymore.
This is to prepare for an upcoming internal change that affects timers.
lovr.graphics.fill renders a fullscreen quad, it's convenient because
you don't need to set up a mesh and toggle all the pipeline states.
However, if you are dealing with copying/rendering between stereo
textures, you have to write your own shader for that. For now.
l_event.c was processing a thread-related event and in the process using a thread struct, even when LOVR_ENABLE_THREAD is undefined and threads do not exist. I caused the thread event type to simply not exist when the thread module is not being built.
Since the event is now only sometimes present, I put it at the end of the enum as slight protection against binary mismatches with dynamically loaded modules.
Returns the predicted display time, which is the estimated time at which
the photons of the next frame will hit the eyeballs of a person in the HMD.
This should be used instead of lovr.timer.getTime when used for rendering
something that is time-dependent. Updating simulations, logic, or access
to high frequency times should still use lovr.timer.getTime.
- Add t.hotkeys flag to conf.lua, which defaults to true.
- If t.hotkeys is truthy, the following hotkeys will be enabled:
- Escape: Quit the experience
- F5: Restart the experience
- Ref struct only stores refcount now and is more general.
- Proxy stores a hash of its type name instead of an enum.
- Variants store additional information instead of using a vtable.
- Remove the concept of superclasses from the API.
- Clean up some miscellaneous includes.
The iterator is nice and concise, but a table is more conventional
and allows you to easily retrieve the number of tracked hands. The
iterator version may be removed in favor of the table version, since
you can always iterate over a table yourself.
If we expose both unhanded hands and handed hands, people need to
deal with handling (haha) both cases in their apps. It's simpler
to always deal with left and right hands, even though it is a bit
less general. Still, this is congruent with the current state of
OpenVR and OpenXR, and I think there are still open questions about
the more uncommon cases where there are more than two hands.
newShader and newComputeShader accept an optional options table
that can contain a table of flags. Flags are turned into #defines
in the shader source and can be used as specialization constants in
the future.
Currently they can only be bools and ints. This might change to add
float and/or remove bools.
- Add vsync flag to t.window and lovr.graphics.createWindow.
- vsync is 1 by default.
- Some headset drivers override vsync if they have special timing requirements.
It's probably slower, but it's way less complicated, so it seems like
a win for now. Can implement compile-time hashing, tries etc. later
if it's identified as a performance issue for a real person.
Also a bunch of cleanup in openvr.c and oculus.c.