Skip to content

tes3vector3⚓︎

A simple trio of floating-point numbers.

Properties⚓︎

b⚓︎

The third value in the vector. An alias for z.

Returns:

  • result (number)

g⚓︎

The second value in the vector. An alias for y.

Returns:

  • result (number)

r⚓︎

The first value in the vector. An alias for x.

Returns:

  • result (number)

x⚓︎

The first value in the vector.

Returns:

  • result (number)

y⚓︎

The second value in the vector.

Returns:

  • result (number)

z⚓︎

The third value in the vector.

Returns:

  • result (number)

Methods⚓︎

__tostring⚓︎

Converts the vector to a string with 2 decimal places.

local result = myObject:__tostring()

Returns:

  • result (string)

angle⚓︎

The returns the angle between the two vectors in radians.

local result = myObject:angle(vec)

Parameters:

Returns:

  • result (number)

copy⚓︎

Creates a copy of the vector.

local result = myObject:copy()

Returns:


cross⚓︎

Calculates the cross product with another vector.

local result = myObject:cross(vec)

Parameters:

Returns:


distance⚓︎

Calculates the distance to another vector in the standard way, i.e., using the Euclidean distance.

local result = myObject:distance(vec)

Parameters:

Returns:

  • result (number)

distanceChebyshev⚓︎

Calculates the distance to another vector, using the Chebyshev metric, which is defined as

math.max(math.abs(v1.x - v2.x), math.abs(v1.y - v2.y), math.abs(v1.z - v2.z))

This is useful for ensuring that the x, y, and z coordinates between two vectors are all (independently) within a certain distance from each other.

Here is a geometric description of the difference between the normal distance and the Chebyshev distance for two tes3vector3s v1 and v2:

  • If v1:distance(v2) <= 1, then v2 is contained in a sphere around v1 with radius 1 (i.e. diameter 2).
  • If v1:distanceChebyshev(v2) <= 1, then v2 is contained within a cube centered around v1, where the cube has length 2.
local result = myObject:distanceChebyshev(vec)

Parameters:

Returns:

  • result (number)

distanceManhattan⚓︎

Calculates the distance to another vector, using the Manhattan (i.e. city block) metric. In the two-dimensional case, the Manhattan metric can be thought of as the distance that two taxis will have to travel if they're following a grid system. The formula for the Manhattan distance is

math.abs(v1.x - v2.x) + math.abs(v1.y - v2.y) + math.abs(v1.z - v2.z)

This is useful for checking how far you'd actually have to move if you're only allowed to move along one axis at a time.

local result = myObject:distanceManhattan(vec)

Parameters:

Returns:

  • result (number)

distanceXY⚓︎

Calculates the distance between the XY-coordinates of two vectors.

This method offers a way of calculating distances between vectors in situations where it's more convenient to ignore the z-coordinates.

local result = myObject:distanceXY(vec)

Parameters:

Returns:

  • result (number)
Example: Items on bookshelves.

Let's say you want to make a function that checks if two ingredients are close together. This will involve looking at the distance between two tes3references.

One way to do this would be to use the normal tes3vector3:distance method, but this has a drawback: it doesn't work consistently with ingredients on bookshelves. If two ingredients are on the same shelf, their z-coordinates contribute very little to the distance between them, while the situation is reversed for ingredients on different shelves.

This problem is remedied by using tes3vector3:distanceXY as follows:

-- Check if two items are on the same bookshelf
---@param ref1 tes3reference reference to one ingredient
---@param ref2 tes3reference reference to another ingredient
local function isOnSameShelf(ref1, ref2)
    local maxDistXY = 75 -- XY tolerance
    local maxDistZ = 150 -- Z tolerance

    -- distanceXY ignores the Z coordinate, which has the effect of 
    -- "pretending" `ref1` and `ref2` are on the same shelf
    local distanceXY = ref1.position:distanceXY(ref2.position)
    -- check the height difference separately, to make sure it's not too crazy 
    -- for example, if we're inside a building, this would make sure that 
    --  `ref1` and `ref2` are on the same floor of the building.
    local distanceZ = ref1.position:heightDifference(ref2.position)

    return distanceXY <= maxDistXY and distanceZ <= maxDistZ
end

dot⚓︎

Calculates the dot product with another vector.

local result = myObject:dot(vec)

Parameters:

Returns:

  • result (number)
Example: The visualization of vector reflection

outDirection = inDirection - (normal * inDirection:dot(normal) * 2)

---@type niSwitchNode, niSwitchNode, niCamera
local lineIn, lineOut, camera
local verticalOffset = tes3vector3.new(0, 0, -30)

local function onLoaded()
    -- MWSE ships with a mesh which contains a few useful widgets.
    -- These can be used during debugging.
    local mesh = tes3.loadMesh("mwse\\widgets.nif") --[[@as niNode]]
    local widgets = {
        -- 3D coordinate axes
        arrows = mesh:getObjectByName("unitArrows") --[[@as niTriShape]],
        -- A common switch node that has three almost infinite lines
        -- along each coordinate exis
        axes = mesh:getObjectByName("axisLines") --[[@as niSwitchNode]],
        plane = mesh:getObjectByName("unitPlane") --[[@as niTriShape]],
        sphere = mesh:getObjectByName("unitSphere") --[[@as niTriShape]]
    }

    local root = tes3.worldController.vfxManager.worldVFXRoot
    ---@cast root niNode

    lineIn = widgets.axes:clone() --[[@as niSwitchNode]]
    lineOut = lineIn:clone() --[[@as niSwitchNode]]

    root:attachChild(lineIn)
    root:attachChild(lineOut)
    root:update()

    -- switchIndex = 0 - x axis (red)
    -- switchIndex = 1 - y axis (green)
    -- switchIndex = 2 - z axis (blue)
    lineIn.switchIndex = 1
    lineOut.switchIndex = 1
    camera = tes3.worldController.worldCamera.cameraData.camera
end
event.register(tes3.event.loaded, onLoaded)

local function simulateCallback()
    lineIn.translation = tes3.getPlayerEyePosition() + verticalOffset

    local inDirection = camera.worldDirection
    local rotation = lineIn.rotation:copy()
    rotation:lookAt(inDirection, camera.worldUp)

    lineIn.rotation = rotation
    lineIn:update()

    -- Now get the coordinates for the outLine
    --local inDirection = tes3.getPlayerEyeVector()
    local hit = tes3.rayTest({
        position = lineIn.translation,
        direction = inDirection,
        returnNormal = true,
        returnSmoothNormal = true,
        ignore = { tes3.player, tes3.player1stPerson }
    })
    if not hit then return end

    lineOut.translation = hit.intersection

    local normal = hit.normal
    local outDirection = inDirection - (normal * inDirection:dot(normal) * 2)
    outDirection:normalize()
    local axis = outDirection:cross(inDirection)
    local rotation = tes3matrix33.new()
    rotation:lookAt(outDirection, axis:normalized())

    lineOut.rotation = rotation
    lineOut:update()
end
event.register(tes3.event.simulate, simulateCallback)

heightDifference⚓︎

Calculates the vertical distance to another vector.

local result = myObject:heightDifference(vec)

Parameters:

Returns:

  • result (number)

interpolate⚓︎

Calculates the interpolated position against the target vector using the distance parameter.

local result = myObject:interpolate(targetPoint, distance)

Parameters:

Returns:


length⚓︎

Calculates the length of the vector.

local result = myObject:length()

Returns:

  • result (number)

lerp⚓︎

Calculates the interpolated vector between this vector and another, given a transition.

local lerpedVector = myObject:lerp(toVector, transition)

Parameters:

  • toVector (tes3vector3): The vector to interpolate towards.
  • transition (number): The interpolation value. Must be between 0.0 (closer to this vector) and 1.0 (closer to the other vector).

Returns:


negate⚓︎

Negates all values in the vector.

myObject:negate()

normalize⚓︎

Normalize the vector in-place, or set its components to zero if normalization is not possible. Returns true if the vector was successfully normalized.

local result = myObject:normalize()

Returns:

  • result (boolean)

normalized⚓︎

Get a normalized copy of the vector.

local result = myObject:normalized()

Returns:


outerProduct⚓︎

Calculates the outer product with another vector.

local result = myObject:outerProduct(vec)

Parameters:

Returns:


toColor⚓︎

Converts the vector to niColor object.

local result = myObject:toColor()

Returns:


Functions⚓︎

new⚓︎

Creates a new vector. If no parameters are provided, an empty set will be constructed.

local vector = tes3vector3.new(x, y, z)

Parameters:

  • x (number): Default: 0.
  • y (number): Default: 0.
  • z (number): Default: 0.

Returns:


Math Operations⚓︎

Addition (+)⚓︎

Left operand type Right operand type Result type Description
tes3vector3 number tes3vector3 Add the given number to each of the vector's components.
tes3vector3 tes3vector3 tes3vector3 Standard vector addition.

Division (/)⚓︎

Left operand type Right operand type Result type Description
tes3vector3 number tes3vector3 Divides the vector by a scalar.

Length (#)⚓︎

Result type Description
number Evaluates to the vector's length in game units.

Multiplication (*)⚓︎

Left operand type Right operand type Result type Description
tes3vector3 tes3vector3 tes3vector3 The per-element multiplication of two vectors, also known as Hadamard product.
tes3vector3 number tes3vector3 Multiplies the vector by a scalar.

Subtraction (-)⚓︎

Left operand type Right operand type Result type Description
tes3vector3 number tes3vector3 Subtracts given number from each of the vector's components.
tes3vector3 tes3vector3 tes3vector3 Standard vector subtraction.

Unary minus (-)⚓︎

Result type Description
tes3vector3 Swaps the sign of the vector's components.