vu

package module
v0.5.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 22, 2015 License: BSD-2-Clause, Zlib Imports: 13 Imported by: 1

README

#Vu

Vu (Virtual Universe) is a minimalist 3D engine written primarily in Go (Golang). Vu is composed of packages, detailed in GoDoc, and briefly summarized below.

Sub packages

  • audio Positions and plays sounds in a 3D environment.
  • audio/al OpenAL bindings. Links the audio layer and the sound hardware.
  • device Links the application to native OS specific window and user events.
  • load Asset loaders including models, textures, audio, shaders, and bitmapped fonts.
  • math/lin Vector, matrix, quaternion, and transform linear math library.
  • move Repositions bodies based on simulated physics.
  • render 3D drawing and graphics interface.
  • render/gl Generated OpenGL bindings. Links rendering system to graphics hardware.
  • render/gl/gen OpenGL binding generator.

Less essential, but potentially more fun packages are:

  • eg Examples that both demonstrate and validate the vu engine.
  • ai Behaviour Tree for autonomous units.
  • form 2D GUI layout helper.
  • grid Grid based random level generators. A-star and flow field pathfinding.
  • land Height map and land surface generator.

Installation

Ensure you have installed Go > 1.3:

go get -u github.com/gazed/vu

Now you can build and run examples:

cd $GOPATH/src/github.com/gazed/vu/eg
go build .
./eg

Build Dependencies

  • OS X: Objective C and C compilers (clang) from Xcode command line tools.
  • Windows: C compiler (gcc) from mingw64-bit.

Runtime Dependencies

  • OpenGL version 3.3 or later.
  • OpenAL 64-bit version 2.1.

Building on Windows

  • Bampf has been built and tested on Windows using gcc from mingw64-bit. Mingw64 was installed to c:/mingw64.
    • Put OpenAL on the gcc library path by copying openal-soft-1.15.1-bin/Win64/soft_oal.dll to c:/mingw64/x86_64-w64-mingw32/lib/OpenAL32.dll
  • 64-bit OpenAL may be difficult to locate for Windows machines. Try http://kcat.strangesoft.net/openal.html/openal-soft-1.15.1-bin.zip.
    • Extract Win64/soft_oal.dll from the zip to c:/Windows/System32/OpenAL32.dll.
  • Building with Cygwin has not been attempted. It may have special needs.

Limitations

The engine and its packages include the essentials by design. In particular:

  • There is no 3D editor.
  • There is no networking package.
  • Physics only handles boxes and spheres.
  • The device layer interface provides only the absolute minimum from the underlying windowing system. Only OSX, Windows 7 and 8 are currently supported.
  • Rendering supports standard OpenGL 3.3 and later. OpenGL extensions are not used.
  • Windows is limited by the availability of OpenGL and OpenAL. Generally OpenGL issues are fixed by downloading manufacturer's graphic card drivers. However older laptops with Intel graphics don't always have OpenGL drivers.

Documentation

Overview

Package vu, virtual universe, provides 3D application support. Vu wraps subsystems like rendering, physics, data loading, audio, etc. to provide higher level functionality that includes:

  • Transform graphs and composite objects.
  • Timestepped update/render loop.
  • Access to user input events.
  • Cameras and transform manipulation.
  • Coupling loaded assets to render and audio systems.

Refer to the vu/eg package for examples of engine functionality.

Vu dependencies are:

  • OpenGL for graphics card access. See package vu/render.
  • OpenAL for sound card access. See package vu/audio.
  • Cocoa for OSX windowing and input. See package vu/device.
  • WinAPI for Windows windowing and input. See package vu/device.

Index

Constants

View Source
const (

	// Global graphic state constants. See Eng.State
	BLEND = render.BLEND // Alpha blending. Enabled by default.
	CULL  = render.CULL  // Backface culling. Enabled by default.
	DEPTH = render.DEPTH // Z-buffer awareness. Enabled by default.

	// Per-part rendering constants for Model.SetDrawMode.
	TRIANGLES = render.TRIANGLES // Triangles are the norm.
	POINTS    = render.POINTS    // Used for particle effects.
	LINES     = render.LINES     // Used for drawing squares and boxes.

	// User input key released indicator. Total time down, in update
	// ticks, is key down ticks minus RELEASED. See App.Update.
	RELEASED = device.KEY_RELEASED

	// Render buckets. Lower values drawn first.
	OPAQUE      = render.OPAQUE      // draw first
	TRANSPARENT = render.TRANSPARENT // draw after opaque
	OVERLAY     = render.OVERLAY     // draw last.

	// Texture rendering directives for Model.SetTexMode()
	TEX_REPEAT = iota // Repeat texture when UV greater than 1.
	TEX_CLAMP         // Clamp to texture edge.

	// 3D Direction constants. Primarily used for panning or
	// rotating a camera view. See Camera.Spin.
	XAxis // Affect only the X axis.
	YAxis // Affect only the Y axis.
	ZAxis // Affect only the Z axis.

	// Objects created by the application.
	// Note that POV is both a transform hierarchy node
	// and a particular location:orientation in 3D space.
	POV   // Transform hierarchy node and 3D location:orientation.
	MODEL // Rendered model attached to a Pov.
	BODY  // Physics body attached to a Pov.
	VIEW  // Camera and view transform attached to a Pov.
	NOISE // Sound attached to a Pov.
	LIGHT // Light attached to a Pov.
)

Engine constants used as input to various methods.

Variables

This section is empty.

Functions

func Cast

func Cast(ray, b move.Body) (hit bool, x, y, z float64)

Cast checks if a ray r intersects the given Body b, returning the nearest point of intersection if there is one. The point of contact x, y, z is valid when hit is true.

func New

func New(app App, name string, wx, wy, ww, wh int) (err error)

New creates the Engine and initializes the underlying resources needed by the engine. It then starts application callbacks through the App interface. This is expected to be called once on application startup.

func NewBox

func NewBox(hx, hy, hz float64) move.Body

NewBox creates a box shaped physics body located at the origin. The box size is w=2*hx, h=2*hy, d=2*hz. Used in Part.SetBody()

func NewPlane

func NewPlane(nx, ny, nz float64) move.Body

NewPlane creates a plane located on the origin and oriented by the plane normal nx, ny, nz. Used in Part.SetForm()

func NewRay

func NewRay(dx, dy, dz float64) move.Body

NewRay creates a ray located at the origin and pointing in the direction dx, dy, dz. Used in Part.SetForm()

func NewSphere

func NewSphere(radius float64) move.Body

NewSphere creates a ball shaped physics body located at the origin. The sphere size is defined by the radius. Used in Part.SetBody()

func SetRay

func SetRay(ray move.Body, x, y, z float64)

SetRay updates the ray direction.

func VO

func VO(pov *lin.T, scr *lin.Q, vm *lin.M4) *lin.M4

VO orthographic view transform.

func VP

func VP(at *lin.T, scr *lin.Q, vm *lin.M4) *lin.M4

VP perspective view transform.

func XZ_XY

func XZ_XY(at *lin.T, scr *lin.Q, vm *lin.M4) *lin.M4

XZ_XY perspective to ortho view transform. Can help transform a 3D map to a 2D overlay.

Types

type App

type App interface {
	Create(eng Eng, s *State) // One time call after successfull startup.

	// Update allows applications to change state prior to the next render.
	// Update is called many times a second after the initial call to Create.
	//      i : user input refreshed prior to each call.
	//      s : engine state refreshed prior to each call.
	Update(eng Eng, i *Input, s *State) // Process user input.
}

App is the engine callback expected to be implemented by the application. The application is registered on engine creation as follows:

err := vu.New(app, "Title", 0, 0, 800, 600)

Note that it is safe to call Eng methods from goroutines.

type Camera

type Camera interface {
	Location() (x, y, z float64)    // Get, or
	SetLocation(x, y, z float64)    // ...Set the camera location.
	Move(x, y, z float64, q *lin.Q) // Adjust location along orientation.

	// Orientation is calculated from pitch and yaw.
	Lookat() *lin.Q          // Get the XYZ view orientation.
	Lookxz() *lin.Q          // Get quaternion Y rotation.
	Pitch() (deg float64)    // Get or...
	SetPitch(deg float64)    // ...Set the X rotation in degrees.
	AdjustPitch(deg float64) // ...adjust rotation around X axis.
	Yaw() (deg float64)      // Get or...
	SetYaw(deg float64)      // ...Set the Y rotation in degrees.
	AdjustYaw(deg float64)   // ...adjust rotation around Y axis.

	// Use one of the following to create a projection transform.
	SetPerspective(fov, ratio, near, far float64)                // 3D.
	SetOrthographic(left, right, bottom, top, near, far float64) // 2D.

	// Set the algorithim to create the view matrix.
	SetTransform(vt ViewTransform) // Update the view and inverse view.

	// Ray applies inverse transforms to derive world space coordinates for
	// a ray projected from the camera through the mouse's mx, my screen
	// position given window width and height ww, wh.
	Ray(mx, my, ww, wh int) (x, y, z float64)

	// Screen calculates screen coordinates sx, sy for world coordinates
	// wx, wy, wz and window width and height ww, wh.
	Screen(wx, wy, wz float64, ww, wh int) (sx, sy int)

	// Distance returns the distance squared of the camera to the given point.
	Distance(px, py, pz float64) float64
}

Camera tracks the location and orientation of a camera as well as an associated projection transform. It is ultimately used to set the view portion of the transform matricies for models. While cameras are associated with a Pov in the transform hierarchy, they keep their own location and orientation which allows them to be positioned independently from the transform hierarchy models.

type Cull

type Cull interface {

	// Cull returns true if a model represented by point, px, py, pz
	// should be culled using the given camera.
	Cull(cam Camera, px, py, pz float64) bool
}

Cull is attached to a View in order to reduce the number of items sent for rendering.

func NewFrontCull

func NewFrontCull(r float64) Cull

NewFrontCull returns a culler that keeps objects in a radius directly in front of the camera. Objects behind the camera and far away from the camera are culled.

func NewRadiusCull

func NewRadiusCull(r float64) Cull

NewRadiusCull returns a culler that removes objects outside a given radius from the camera. Can be used for show objects around the camera for top down minimaps.

type Effect

type Effect func(all []*EffectParticle, dt float64) (live []*EffectParticle)

Effect describes the application provided particle effect that updates the list of potential particles and returns the active particle set. Delta-time, dt, is the elapsed time in seconds since the last update. This is used for CPU particle effects where the application does the majority of the work controlling particle lifespans and positions in the provided Effect method.

An effect is expected to be attached to a Model to it can be positioned and rendered.

type EffectParticle

type EffectParticle struct {
	Index   float32 // Particle number.
	Alive   float32 // Goes from 1:new particle, to 0:dead.
	X, Y, Z float64 // Particle location.
}

EffectParticle is one of the particles updated by an Effect. A set of these are returned by the Effect update method and are rendered by the engine.

type Eng

type Eng interface {
	Shutdown()     // Stop the engine and free allocated resources.
	Reset()        // Put the engine back to its initial state.
	State() *State // Query engine state. State updated per tick.
	Root() Pov     // Single root transform always exists.

	// Timing is updated each processing loop. The returned update
	// times can flucuate and should be averaged over multiple calls.
	Usage() *Timing                // Per update loop performance metrics.
	Modelled() (models, verts int) // Total render models and verticies.
	Rendered() (models, verts int) // Rendered models and verticies.

	// Requests to change engine state.
	SetColor(r, g, b, a float32)      // Set background clear colour.
	ShowCursor(show bool)             // Hide or show the cursor.
	SetCursorAt(x, y int)             // Put cursor at the window pixel x,y.
	Enable(attr uint32, enabled bool) // Enable/disable render attributes.
	ToggleFullScreen()                // Flips full screen and windowed mode.
	Mute(mute bool)                   // Toggle sound volume.
	SetVolume(zeroToOne float64)      // Set sound volume.
	SetGravity(g float64)             // Change the gravity constant.
}

Eng provides support for a 3D application conforming to the App interface. Eng provides the root transform hierarchy node, a point-of-view (Pov) where models, physics bodies, and noises are attached and processed each update. Eng is also used to set global engine state and provides top level timing statistics.

type Input

type Input struct {
	Mx, My  int            // Current mouse location.
	Down    map[string]int // Keys, buttons with down duration ticks.
	Focus   bool           // True if window is in focus.
	Resized bool           // True if window was resized or moved.
	Scroll  int            // Scroll amount, if any.
	Dt      float64        // Delta time for this update.
	Ut      uint64         // Total number of update ticks.
}

Input is used to communicate current user input to the application. This gives the current cursor location, current pressed keys, mouse buttons, and modifiers. These are sent to the application using the App.Update() callback.

The map of keys and mouse buttons that are currently pressed also include how long they have been pressed in update ticks. A negative value indicates a release. The total down duration can then be calculated by down duration less RELEASED timestamp.

type Light

type Light interface {
	Colour() (r, g, b float64)       // Get light colour.
	SetColour(r, g, b float64) Light // Set light colour.
}

Light is attached to a Pov to give it a position in world space. It is used by shaders to interact with a models material values. Light is defaulted to white 1, 1, 1. Valid r, g, b colour values are between 0 and 1.

type Model

type Model interface {
	Shader() (name string)     // Rendered models have a shader.
	LoadMat(name string) Model // Optional surface material colours.
	Alpha() (a float64)        // Transparency: 1 for full opaque
	SetAlpha(a float64)        // ...0 for fully transparent.
	Colour() (r, g, b float64) // Colors between 0 and 1.
	SetColour(r, g, b float64) // ...1 for full colour.

	// Mesh handles verticies, per-vertex data, and triangle faces.
	// Meshes can be loaded from assets or created/generated.
	// LoadMesh creates a mesh from loaded mesh resource assets.
	LoadMesh(name string) Model // Expects to load static mesh data.
	// NewMesh creates an empty mesh expecting generated data.
	NewMesh(name string) Model // App is responsible for generating data.
	// Generating mesh data is closely tied to a given shader.
	// InitMesh must be called once, SetMesh data may be called as needed.
	//    lloc     : layout location is the shader input reference.
	//    span     : indicates the number of data points per vertex.
	//    usage    : STATIC or DYNAMIC.
	//    normalize: true to convert data to the 0->1 range.
	// Some vertex shader data conventions are:
	//    Vertex positions lloc=0 span=3_floats_per_vertex.
	//    Vertex normals   lloc=1 span=3_floats_per_vertex.
	//    UV tex coords    lloc=2 span=2_floats_per_vertex.
	//    Colours          lloc=3 span=4_floats_per_vertex.
	InitMesh(lloc, span, usage uint32, normalize bool) Model
	SetMeshData(lloc uint32, data interface{}) // Only works after InitMesh
	InitFaces(usage uint32) Model              // Defaults to STATIC_DRAW
	SetFaces(data []uint16)                    // Indicies to vertex positions.
	SetDrawMode(mode int) Model                // TRIANGLES, LINES, POINTS.

	// Models can have one or more textures applied to a single mesh.
	// Textures are initialized from assets and can be altered using images.
	AddTex(texture string) Model          // Loads and adds a texture.
	SetTex(index int, name string)        // Replace texture.
	SetImg(index int, img image.Image)    // Replace image, ignore nil.
	TexImg(index int) image.Image         // Get image, nil if invalid index.
	SetTexMode(index int, mode int) Model // TEX_CLAMP, TEX_REPEAT.

	// Animated models can have multiple animated sequences,
	// ie. "moves", that are indexed from 0. Bones can also
	// be used to position other models, ie: attachment points.
	LoadAnim(anim string) Model            // Sets an animated model.
	Animate(action, frame int) bool        // Return true if available.
	Action() (action, frame, maxFrame int) // Current movement info.
	Actions() []string                     // Animation sequence names.
	Pose(bone int) *lin.M4                 // Bone transform: attach point.

	// Fonts are used to display small text phrases using a mesh plane.
	// Fonts imply a texture shader and a texture for this model.
	LoadFont(font string) Model  // Set the character mapping resource.
	SetPhrase(text string) Model // Set the string to display.
	PhraseWidth() int            // Width in pixels, 0 if not loaded.

	// Particle effects are either CPU:application or GPU:shader based.
	// SetEffect sets a CPU controlled particle effect.
	SetEffect(mover Effect, maxParticles int) Model

	// Set or get custom shader uniform values.
	// The id is the uniform name from the shader.
	Uniform(id string) (value []float32)         // Uniform name/values.
	SetUniform(id string, floats ...interface{}) // Individual values.
}

Model manages rendered 3D objects. Model is the link between loaded assets and the data needed by the rendering system. Assets, such as shaders, mesh, textures, etc. are specified as unique strings in the Load* methods. Assets are loaded and converted to render data before the model is sent for rendering.

A Model is expected to be attached to a point-of-view (Pov) to give it a 3D location and orientation in the render transform hierarchy.

type Noise

type Noise interface {
	Add(sound string) // Loads and adds a sound.
	Play(index int)   // Play. Loaded and bound sounds only.
}

Noise manages sounds associated with a singe Pov. Each sound must be loaded with sound data that has been bound to the audio card in order for the sound can be played.

type Pov

type Pov interface {
	Location() (x, y, z float64)     // Get, or
	SetLocation(x, y, z float64) Pov // ...Set the current location.
	Rotation() (q *lin.Q)            // Get, or
	SetRotation(q *lin.Q)            // ...Set current quaternion rotation.
	Spin(x, y, z float64)            // Rotate degrees about the given axis.
	Move(x, y, z float64, q *lin.Q)  // Move along indicated direction.

	// Visible affects this transform and any child transforms.
	Visible() bool           // Invisible transforms are removed from
	SetVisible(visible bool) // ...rendering without disposing them.

	// Per axis scale. Normal is 1, greater than 1 to enlarge,
	// positive fraction to shrink.
	Scale() (x, y, z float64)     // Get, or
	SetScale(x, y, z float64) Pov // ...Set the current scale.

	// Create a child POV from this pov.
	NewPov() Pov      // Attaches a new child transform Pov to p.
	Dispose(kind int) // Discard POV, MODEL, BODY, VIEW, or NOISE.

	// Adding a camera to a Pov means that all rendered models in the povs
	// hierarchy will be viewed with the views camera settings.
	View() View    // Nil if no view for this Pov.
	NewView() View // View for the group of this Pov's child models.

	// Model is an optional rendered component associated with a Pov.
	Model() Model                 // Nil if no model.
	NewModel(shader string) Model // Nil if a model already exists.

	// Body is an optional physics component associated with a Pov. Bodies
	// are set at top level Pov transforms to have valid world coordindates.
	Body() move.Body               // Nil if no body.
	NewBody(b move.Body) move.Body // Create non-colliding body.
	SetSolid(mass, bounce float64) // Make existing body collide.

	// Sound is an optional audio component. Played sounds occur at the
	// associated Pov's location. Sounds that are played will be louder
	// as the distance between the played sound and listener decreases.
	Noise() Noise    // Nil if no sound.
	NewNoise() Noise // Create noise. Nil if noise already exists.
	SetListener()    // Place the single global sound listener at this pov.

	// Light is an optional component associated with a Pov.
	Light() Light    // Nil if no light for this Pov.
	NewLight() Light // Create a light at this Pov.
}

Points of view, Pov's, are combinations of positions and orientations. Created by the application, they may have associated components, like rendered models and physics bodies. The associated objects use the location and orientation from the Pov. A Pov can also have a child Pov whose position and orientation is relative to the parent. A hiearchy of parent-child Pov's forms a transform hierarchy.

type State

type State struct {
	X, Y, W, H int     // Window lower left corner and size in pixels.
	R, G, B, A float32 // Background clear colour.
	Cursor     bool    // True when cursor is visible.
	CullBacks  bool    // True to set backface culling on.
	Blend      bool    // True for texture blending.
	FullScreen bool    // True when window is full screen.
	Mute       bool    // True when audio is muted.
}

State is used to communicate current engine wide variable settings. It is refreshed each update and provided to the application.

func (*State) Screen

func (s *State) Screen() (x, y, w, h int)

Screen is a convenience method returning the current window dimensions.

type Surface

type Surface interface {
	Pts() [][]SurfacePoint      // Per vertex information.
	Update(m Model, xo, yo int) // Generates rendering data into Model.
	Resize(w, h int)            // Resize the surface point holders.
}

Surface renders land height data. The surface is rendered based on the height and texture index information in SurfacePoints. Surface populates a render Models mesh data.

func NewSurface

func NewSurface(sx, sy, spread int, textureRatio, scale float32) Surface

NewSurface creates a surface that holds a sx by sy set of SurfacePoints.

spread       : the number of tiles one texture covers.
textureRatio : the size of one texture to the size of the texture atlas.
scale        : the amount of scaling applied to each height.

type SurfacePoint

type SurfacePoint struct {
	Height float32 // Surface height value.
	Tindex int     // Surface texture atlas index.
	Blend  float32 // Texture blend value between 0 and 1.
}

SurfacePoint stores a height value and a texture atlas index for one point in a Surface. Blend indicates the amount of blending of the texture at the given index with the next texture in the atlas.

type Timing

type Timing struct {
	Elapsed time.Duration // Total loop time since last update.
	Update  time.Duration // Time used for previous state update.
	Renders int           // Render requests since last update.
}

Timing is used to collect main processing loop numbers while the the application loop is active. The numbers are reset each update. Applications are expected to track and smooth these per/update values over a number of updates.

Timing gives a rough idea. Expect things to go slower the more models, draw calls, and the greater number of verticies.

FPS = Renders/Elapsed. This is how many render requests were sent. Actual number of renders is likely at the monitor refresh rate which is 60 for most flat screen monitors.

func (*Timing) Dump

func (t *Timing) Dump()

Dump current amount of update loop time tracked in milliseconds. Times is expected to be reset each update.

func (*Timing) Zero

func (t *Timing) Zero()

Zero all time and counter values.

type View

type View interface {
	Cam() Camera             // A single camera for a group of Models.
	Visible() bool           // Only visible views are rendered.
	SetVisible(visible bool) // Whether or not the view is rendered.
	SetDepth(enabled bool)   // True for 3D views. 2D views ignore depth.
	SetUI()                  // UI view: 2D, no depth, drawn last.
	SetLast(index int)       // For sequencing UI views. Higher is later.

	// SetCull sets a method that reduces the number of Models rendered
	// each update. It can be engine supplied ie: NewFacingCuller,
	// or application supplied.
	SetCull(c Cull) // Set to nil to turn off culling.
}

View dictates how models are rendered. A view is attached to a Pov where it renders all models in that Pov's hierarchy. It is possible to attache multiple views, even to the same point. All the models in each visible view are rendered each update.

type ViewTransform

type ViewTransform func(*lin.T, *lin.Q, *lin.M4) *lin.M4

ViewTransform creates a transform matrix from location and orientation. This is expected to be used for camera transforms. The camera is thought of as being at 0, 0, 0. This means moving the camera forward by x:units really means moving the world (everything else) back -x:units. Likewise rotating the camera by x:degrees really means rotating the world by -x.

Directories

Path Synopsis
Package ai provides support for application unit behaviour.
Package ai provides support for application unit behaviour.
Package audio provides access to 3D sound capability.
Package audio provides access to 3D sound capability.
al
Package al provides golang audio library bindings for OpenAL.
Package al provides golang audio library bindings for OpenAL.
Package device provides minimal platform/os access to a 3D rendering context and user input.
Package device provides minimal platform/os access to a 3D rendering context and user input.
Package eg is used to test and demonstrate different aspects of the vu (virtual universe) engine.
Package eg is used to test and demonstrate different aspects of the vu (virtual universe) engine.
Package form is used to divide a 2D area into sections using plans.
Package form is used to divide a 2D area into sections using plans.
Package grid is used to generate layout data for random maze or skirmish levels.
Package grid is used to generate layout data for random maze or skirmish levels.
Package land is used to procedurally generate world terrain height maps.
Package land is used to procedurally generate world terrain height maps.
Package load fetches disk based data that will be used for 3D assets.
Package load fetches disk based data that will be used for 3D assets.
math
lin
Package lin provides a linear math library that includes vectors, matrices, quaternions, transforms and some utility functions.
Package lin provides a linear math library that includes vectors, matrices, quaternions, transforms and some utility functions.
Move is a real-time simulation of real-world physics.
Move is a real-time simulation of real-world physics.
Package render provides access to 3D graphics.
Package render provides access to 3D graphics.
gl
Package gl provides golang bindings for OpenGL The bindings were generated from OpenGL spec src/vu/render/gl/gen/glcorearb.h-v4_3.
Package gl provides golang bindings for OpenGL The bindings were generated from OpenGL spec src/vu/render/gl/gen/glcorearb.h-v4_3.
gl/gen
Package gen is used to generate golang OpenGL bindings using an OpenGL specification header file.
Package gen is used to generate golang OpenGL bindings using an OpenGL specification header file.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL