               smoothFactor = paletteColor.reflection.smoothFactor or 0
 
               # Average the normal.
-              averageNormal = new THREE.Vector3
+              @_averageNormal.set 0, 0, 0
 
               for offsetX in [-smoothFactor..smoothFactor]
                 for offsetY in [-smoothFactor..smoothFactor]
                   if sampleNormal = layer._pixelMap[pixel.x + offsetX]?[pixel.y + offsetY]?.normal
-                    averageNormal.add sampleNormal
-
-              averageNormal.normalize()
-              averageNormal.x *= -1 if flippedHorizontal
-
-              averageNormalLightProduct = THREE.Math.clamp averageNormal.dot(inverseLightDirection), 0, 1
+                    @_averageNormal.add sampleNormal
+
+              @_averageNormal.normalize()
+              @_averageNormal.x *= -1 if flippedHorizontal
+
+              averageNormalLightProduct = THREE.Math.clamp @_averageNormal.dot(inverseLightDirection), 0, 1
 
               # Calculate the perfectly reflected ray.
-              reflection = averageNormal.clone().multiplyScalar(2 * averageNormalLightProduct).sub inverseLightDirection
+              reflection = @_averageNormal.clone().multiplyScalar(2 * averageNormalLightProduct).sub inverseLightDirection
 
               # We assume the inverse view direction to be (0, 0, 1).
               # Dot product is then the equivalent of the z coordinate.
               reflectionViewProduct = THREE.Math.clamp reflection.z, 0, 1
 
               lightFactor = Math.pow(reflectionViewProduct, shininess) * lightSpecularCoefficient * materialSpecularCoefficient
-              lightColor = new THREE.Color(0xffffff).multiplyScalar lightFactor
-
-              shadedColor.add lightColor
+              @_lightColor.set(0xffffff).multiplyScalar lightFactor
+
+              shadedColor.add @_lightColor
 
             if palette
               # Find the nearest color from the palette to represent the shaded color.

        if visualizeNormals
          # Visualized normals mode.
          if pixel.normal
            normal = new THREE.Vector3 pixel.normal.x, pixel.normal.y, pixel.normal.z
            normal.x *= -1 if flippedHorizontal
            backward = new THREE.Vector3 0, 0, 1

            horizontalAngle = Math.atan2(normal.y, normal.x) + Math.PI
            verticalAngle = normal.angleTo backward

            hue = horizontalAngle / (2 * Math.PI)
            saturation = verticalAngle / (Math.PI / 2)


class LOI.Assets.Engine.Sprite
  constructor: (@options) ->
    @palette = new ComputedField =>
      return unless spriteData = @options.spriteData()
      spriteData.customPalette or LOI.Assets.Palette.documents.findOne(spriteData.palette?._id)

    @ready = new ComputedField =>
      return unless spriteData = @options.spriteData()
      return unless spriteData.layers?.length and spriteData.bounds
      return unless @palette() or @options.visualizeNormals?()

      true

    # Create temporary objects and constants.
    @_normal = new THREE.Vector3
    @_backward = new THREE.Vector3 0, 0, 1
    @_destinationColor = new THREE.Color
    @_lightColor = new THREE.Color
    @_averageNormal = new THREE.Vector3

  drawToContext: (context, renderOptions = {}) ->
    # HACK: Request sprite data already at the top since otherwise ready sometimes doesn't get recomputed in time.
