Skip to content

About

A Godot plugin that provides a unified GDScript interface for ML Kit face mesh detection on Android and iOS. Pass any Image to the Vision node and receive rich per-face data including 468 3-D landmark points, triangle mesh indices, and named facial contours - all in a single async call.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

6 stars

Watchers

1 watching

Forks

Latest commit

 

History

6 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

     


Godot Vision Plugin

A Godot plugin that provides a unified GDScript interface for ML Kit face mesh detection on Android and iOS. Pass any Image to the Vision node and receive rich per-face data including 468 3-D landmark points, triangle mesh indices, and named facial contours — all in a single async call.

Key Features:

  • Detect one or more faces in any Image using ML Kit's Face Mesh API; configure the maximum number of faces reported per frame with set_max_faces() (iOS) or rely on the SDK default of 5 (Android)
  • Receive 468 normalised 3-D landmark points per face (x/y ∈ [0, 1], z = relative depth)
  • Access the full triangle mesh (indices into the landmark array) for each detected face
  • Query individual named contours — face oval, eyes, eyebrows, lips, and nose bridge — via FaceMeshInfo constants
  • Built-in wireframe drawing utilities: overlay a mesh on an existing image or generate a transparent mesh layer for compositing
  • Typed result and error classes (FaceScanResult, FaceMeshInfo, ScanError) for clean, idiomatic GDScript

Table of Contents

Demo

Try the demo app located in the demo directory.

Installation

Before installing this plugin, make sure to uninstall any previous versions of the same plugin.

If installing both Android and iOS versions of the plugin in the same project, then make sure that both versions use the same addon interface version.

There are 3 ways to install the Vision plugin into your project:

  • Through the GMP Menu (recommended)
  • Through the Godot Editor's AssetLib
  • Manually by downloading archives from Github

Installing via GMP Menu (recommended)

Steps:

  • install GMP Menu if not installed
  • enable GMP Menu via the Plugins tab of Godot Editor's Project->Project Settings... menu if not enabled
  • select Vision Plugin from Godot Editor's GMP menu
  • select desired plugin version and platform
  • click Download button
  • once the download has completed, click Install button
  • enable Vision via the Plugins tab of Project->Project Settings... menu, in the Godot Editor

Installing via AssetLib

Steps:

  • search for and select the Vision plugin in Godot Editor
  • click Download button
  • on the installation dialog...
    • keep Change Install Folder setting pointing to your project's root directory
    • keep Ignore asset root checkbox checked
    • click Install button
  • enable the plugin via the Plugins tab of Project->Project Settings... menu, in the Godot Editor

Installing both Android and iOS versions of the plugin in the same project

When installing via AssetLib, the installer may display a warning that states "[x number of] files conflict with your project and won't be installed." You can ignore this warning since both versions use the same addon code.

Installing manually

Steps:

  • download release archive from Github
  • unzip the release archive
  • copy to your Godot project's root directory
  • enable the plugin via the Plugins tab of Project->Project Settings... menu, in the Godot Editor

Usage

Add a Vision node to your main scene or an autoload global scene.

  • connect Vision node signals before calling scan_face()
    • face_mesh_ready(result: FaceScanResult) — emitted when detection succeeds
    • face_mesh_failed(error: ScanError) — emitted when detection fails
  • call vision.scan_face(image) with any Image; the image is automatically converted to FORMAT_RGBA8 if needed
  • in the face_mesh_ready callback, iterate over result.get_faces() to access per-face landmark data
  • optionally use the static drawing helpers to render the mesh directly onto an image

Basic face scan example:

@onready var vision := $Vision

func _ready() -> void:
	vision.face_mesh_ready.connect(_on_face_mesh_ready)
	vision.face_mesh_failed.connect(_on_face_mesh_failed)

func scan(image: Image) -> void:
	vision.scan_face(image)

func _on_face_mesh_ready(result: FaceScanResult) -> void:
	print("Detected %d face(s)" % result.get_face_count())
	for face in result.get_faces():
		print("  Points: %d, Triangles: %d" % [face.get_points().size(), face.get_triangles().size()])

func _on_face_mesh_failed(error: ScanError) -> void:
	print("Scan failed [%s]: %s" % [error.get_code(), error.get_description()])

Setting the maximum number of faces (iOS only):

@onready var vision := $Vision

func _ready() -> void:
	vision.face_mesh_ready.connect(_on_face_mesh_ready)
	vision.face_mesh_failed.connect(_on_face_mesh_failed)
	# Detect up to 10 faces per frame.
	# On Android this call is accepted but has no effect;
	# ML Kit's face mesh detector always uses its SDK default (5).
	vision.set_max_faces(10)

func scan(image: Image) -> void:
	vision.scan_face(image)

Drawing the mesh overlay onto an image:

func _on_face_mesh_ready(result: FaceScanResult) -> void:
	# Returns a new Image with the wireframe painted on top
	var annotated: Image = Vision.draw_face_mesh_on_image(original_image, result, Color.CYAN)
	$TextureRect.texture = ImageTexture.create_from_image(annotated)

Generating a transparent mesh layer for compositing:

func _on_face_mesh_ready(result: FaceScanResult) -> void:
	var mesh_layer: Image = Vision.generate_face_mesh_image(
		result, result.get_image().get_width(), result.get_image().get_height(), Color.LIME)
	$MeshOverlay.texture = ImageTexture.create_from_image(mesh_layer)

Accessing individual contours:

func _on_face_mesh_ready(result: FaceScanResult) -> void:
	var face: FaceMeshInfo = result.get_face(0)
	if face:
		var oval: Array = face.get_contour(FaceMeshInfo.CONTOUR_FACE_OVAL)
		for point in oval:
			print("Oval point: ", point)  # Vector3 — x/y normalised, z = depth

Signals

Register listeners to the following signals of the Vision node:

Signal Description
face_mesh_ready(result: FaceScanResult) Emitted when face mesh detection succeeds. result contains the full ImageInfo for the processed image and an array of FaceMeshInfo objects, one per detected face.
face_mesh_failed(error: ScanError) Emitted when detection fails. error contains a ScanError.Code enum value and a human-readable description.

Methods

Vision node methods

Method Description
scan_face(a_image: Image) -> void Sends a_image to the native plugin for ML Kit face mesh detection. The image is automatically converted to FORMAT_RGBA8 internally. Results are delivered asynchronously via the face_mesh_ready or face_mesh_failed signals.
set_max_faces(a_max_faces: int) -> void Sets the maximum number of faces the detector will report per frame. Accepts values in [1, ABSOLUTE_MAX_FACES]; values outside this range are clamped. Defaults to DEFAULT_MAX_FACES (5). iOS: fully supported — MediaPipe rebuilds the landmarker immediately with the new limit. Android: accepted without error but has no effect; ML Kit's FaceMeshDetectorOptions.Builder does not expose a max-faces setting and the SDK default of 5 is always used. The value is persisted and automatically re-applied if the plugin singleton is reconnected after an app-resume.

Vision constants

Constant Value Description
DEFAULT_MAX_FACES 5 Default value used by set_max_faces() and both native plugins on startup.
ABSOLUTE_MAX_FACES 20 Hard upper bound accepted by set_max_faces(); values above this are clamped. Applies to iOS only (see above).

Static drawing utilities

These are pure-GDScript helpers that operate on a FaceScanResult returned by the face_mesh_ready signal. They require no scene nodes or rendering server calls.

Method Description
Vision.generate_face_mesh_image(a_result: FaceScanResult, a_width: int, a_height: int, a_color: Color) -> Image Creates a new FORMAT_RGBA8 image of the given dimensions with a fully transparent background, then draws the face mesh wireframe in a_color. Use this to composite the mesh as a separate layer on top of your camera frame.
Vision.draw_face_mesh_on_image(a_original: Image, a_result: FaceScanResult, a_color: Color) -> Image Returns a new FORMAT_RGBA8 copy of a_original with the face mesh wireframe painted on top in a_color. The original image is not modified.

Classes

FaceScanResult

Encapsulates the full result of a scan_face() call. Received via the face_mesh_ready signal.

Method Return type Description
get_image() ImageInfo Returns the full ImageInfo for the image that was scanned. Use get_image().get_width(), get_image().get_height(), get_image().get_buffer(), etc. to access individual properties. Returns null if the result is malformed.
get_face_count() int Number of faces detected in the image.
get_face(a_index: int) FaceMeshInfo Returns the FaceMeshInfo at the given index, or null if out of range.
get_faces() Array Returns all detected faces as an Array of FaceMeshInfo objects.
is_valid() bool Returns true when all required fields are present in the result.
get_raw_data() Dictionary Returns the underlying raw data dictionary.

FaceMeshInfo

Encapsulates the mesh data for a single detected face.

Method Return type Description
get_points() Array All 468 landmark points as Vector3. x and y are normalised to [0, 1]; z is relative depth.
get_triangles() Array All mesh triangles as Vector3i. Each component (x, y, z) is an index into the get_points() array.
get_contours() Dictionary Raw contours dictionary keyed by contour name. Values are arrays of [x, y, z] sub-arrays.
get_contour(a_contour_name: String) Array Points of a named contour as Array of Vector3. Use the CONTOUR_* constants as the name argument.
is_valid() bool Returns true when points, triangles, and contours are all present.
get_raw_data() Dictionary Returns the underlying raw data dictionary.

Contour name constants (pass to get_contour()):

Constant Facial region
CONTOUR_FACE_OVAL Outer boundary of the face
CONTOUR_LEFT_EYE Left eye outline
CONTOUR_LEFT_EYEBROW_BOTTOM Bottom edge of the left eyebrow
CONTOUR_LEFT_EYEBROW_TOP Top edge of the left eyebrow
CONTOUR_LOWER_LIP_BOTTOM Outer bottom edge of the lower lip
CONTOUR_LOWER_LIP_TOP Inner top edge of the lower lip
CONTOUR_NOSE_BRIDGE Bridge of the nose
CONTOUR_RIGHT_EYE Right eye outline
CONTOUR_RIGHT_EYEBROW_BOTTOM Bottom edge of the right eyebrow
CONTOUR_RIGHT_EYEBROW_TOP Top edge of the right eyebrow
CONTOUR_UPPER_LIP_BOTTOM Inner bottom edge of the upper lip
CONTOUR_UPPER_LIP_TOP Outer top edge of the upper lip

ScanError

Encapsulates error information delivered via the face_mesh_failed signal.

Method Return type Description
get_code() ScanError.Code One of the Code enum values below.
get_description() String Human-readable description of the error.

ScanError.Code enum values:

Value Meaning
NONE No error.
INVALID_IMAGE The supplied image could not be processed.
NO_CODE_DETECTED No face was detected in the image.
SCANNER_FAILURE The underlying ML Kit scanner returned a failure.
INTERNAL_ERROR An unexpected internal error occurred.

ImageInfo

Wraps a Godot Image as a serialisable dictionary for transfer to the native plugin. Used internally by scan_face() — you do not normally need to use this class directly.

Method Return type Description
ImageInfo.create_from_image(a_image: Image) (static) ImageInfo Creates an ImageInfo from a Godot Image.
get_buffer() PackedByteArray Raw pixel bytes of the image.
get_width() int Image width in pixels.
get_height() int Image height in pixels.
get_format() Image.Format Pixel format of the image.
has_mipmaps() bool Whether the image has mipmaps.
is_valid() bool Returns true when width, height, and buffer are all present.
get_raw_data() Dictionary Returns the underlying raw data dictionary.

Platform-Specific Notes

Android

iOS


Links


All Plugins

✦ Plugin Android iOS Latest Release Downloads Stars
Admob ✅ ✅
Connection State ✅ ✅
Deeplink ✅ ✅
Firebase ✅ ✅
In-App Review ✅ ✅
Native Camera ✅ ✅
Notification Scheduler ✅ ✅
OAuth 2.0 ✅ ✅
QR ✅ ✅
Share ✅ ✅
Vision ✅ ✅

Credits

Developed by Cengiz

Based on Godot Mobile Plugin Template v7

Original repository: Godot Vision Plugin


Contributing

Contributions are welcome. Please see the contributing guide in the repository for details.


💖 Support the Project

If this plugin has helped you, consider supporting its development! Every bit of support helps keep the plugin updated and bug-free.

✦ Ways to Help How to do it
✨⭐ Spread the Word Star this repo to help others find it.
💡✨ Give Feedback Open an issue or suggest a feature.
🧩 Contribute Submit a PR to help improve the codebase.
❤️ Buy a Coffee Support the maintainers on GitHub Sponsors or other platforms.

⭐ Star History

Star History Chart

About

A Godot plugin that provides a unified GDScript interface for ML Kit face mesh detection on Android and iOS. Pass any Image to the Vision node and receive rich per-face data including 468 3-D landmark points, triangle mesh indices, and named facial contours - all in a single async call.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

6 stars

Watchers

1 watching

Forks

Releases

Contributors

Languages