4
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Elixirで作った3D表示のアプリが動きます 〜 そう、iPhoneならね 〜

Last updated at Posted at 2025-12-04

iPhoneで手軽に3D表示をしてみたと思ったこと一度はありますよね?
できます
そう、ElixirDesktopならね

実行画面

開発環境

  • M4 Mac mini 16GB
  • macOS Tahoe 26.1
  • Elixir 1.19.3-otp-27
  • Erlang 27.3.4
  • Xcode 26.1.1 (17B100)
  • iPhone 16e
  • iOS 18.6.2

前提知識

ElixirDesktopでのビルド
こちらが参考になります

Elixirでの3D表示

では作業開始

まず、ベースとなる3Dのソースを取得します

こちらの環境構築の部分を試してください

まず実行結果が出ればOKです

ElixirDesktop関連のライブラリを組み込む

前提として一度

で動作確認をしてください
これが進まないとできません

上記コラムの動作チェックができたら

ライブラリーを追加

mix.exs
defmodule Three.MixProject do
  use Mix.Project
# 省略 #
  defp deps do
    [
# 省略 #
-     {:bandit, "~> 1.5"}
+     {:bandit, "~> 1.5"},     
+     {:desktop_setup, github: "thehaigo/desktop_setup", only: :dev}
    ]
  end
# 省略 #
end
$ mix deps.get
$ mix desktop.install

これでライブラリーの取得できました

ルータの書き換え

ルートを表示したいページに書き換え

defmodule ThreeWeb.Router do
  use ThreeWeb, :router

# 省略 #
  scope "/", ThreeWeb do
    pipe_through :browser_s
    
-   get "/", PageController, :home
+   live "/", CgLive.S1, :index
  end

# 省略 #
end

3Dを表示する内容
好きに書き換えてください
これはサンプルです

lib/three_web/live/cg/s1.ex
defmodule ThreeWeb.CgLive.S1 do
  use ThreeWeb, :live_view
  import ThreeWeb.Cg.CgHelper

  # 1列のパネル数
  @plane_count 80
  @plane_x -18
  @plane_s 2

  @impl true
  def mount(_params, _session, socket) do
    Process.send_after(self(), :update, 500)

    socket =
      socket
      |> set_size()
      |> assign(data: initialization_character_data())
      |> add_planes()
      |> add_plane("logo_plane", 0.71 / 2, 0.48 / 2, "#FFFFFF")
      |> position("logo_plane", -4.3, 3.5, 0)
      |> load_texture("logo", "/images/logo.png")
      |> load_texture("t1", "/images/t1.jpg")
      |> load_model("test", "/images/test.vrm")
      |> add_text_plane("my_greeting", "Phoenix Framework", 15, "#CC5500")
      |> position("my_greeting", 0.1, 3.5, 0)

    {:ok, main(socket)}
  end

  @impl true
  def handle_info(:update, socket) do
    Process.send_after(self(), :update, 20)
    {:noreply, main(socket)}
  end

  def handle_event("load_model", %{"name" => "test", "status" => "completion"}, socket) do
    socket =
      socket
      |> position("test", -1, 0, 1)
      |> rotation("test", -2, 0, -1.6)

    {:noreply, socket}
  end

  def handle_event("load_texture", %{"name" => "logo", "status" => "completion"}, socket) do
    socket =
      socket
      |> set_texture("logo_plane", "logo")

    {:noreply, socket}
  end

  def handle_event("load_texture", %{"name" => "t1", "status" => "completion"}, socket) do
    socket =
      socket
      |> set_textures()

    {:noreply, socket}
  end

  defp initialization_character_data() do
    0
  end

  defp main(socket) do
    character_data = update(socket.assigns.data)

    socket
    |> positions(-character_data)
    |> set_text_plane_text(
      "my_greeting",
      "Phoenix Framework  #{DateTime.utc_now()}",
      15,
      "#CC5500"
    )
    |> assign(data: character_data)
  end

  defp update(character_data) do
    if character_data >= 2, do: 0, else: character_data + 0.01
  end

  defp add_planes(socket) do
    Enum.reduce(1..@plane_count, socket, fn x, acc ->
      acc
      |> add_plane("bg_#{x}", @plane_s, @plane_s, "#666666")
      |> add_plane("bg_a#{x}", @plane_s, @plane_s, "#333333")
      |> add_plane("bg_b#{x}", @plane_s, @plane_s, "#111111")
    end)
  end

  defp set_textures(socket) do
    Enum.reduce(1..@plane_count, socket, fn x, acc ->
      set_texture(acc, "bg_#{x}", "t1")
      |> set_texture("bg_a#{x}", "t1")
      |> set_texture("bg_b#{x}", "t1")
    end)
  end

  defp positions(socket, add_x) do
    Enum.reduce(1..@plane_count, socket, fn x, acc ->
      base = @plane_x + x * @plane_s

      bg_x = base + add_x * 64
      bg_ax = base + add_x * 16
      bg_bx = base + add_x * 4

      position(acc, "bg_#{x}", bg_x, -4, -2)
      |> position("bg_a#{x}", bg_ax, -2, -2)
      |> position("bg_b#{x}", bg_bx, 0, -2)
    end)
    |> rotation("test", add_x * 10, 0, -1.6)
    |> rotation_bone("test", "J_Bip_R_UpperArm", add_x, add_x * 10, add_x)
    |> rotation_bone("test", "J_Bip_L_UpperArm", add_x * 10, add_x * -10, add_x)

  end
end

iOSアプリの準備

$ mix desktop.setup.ios
$ cd native/ios
$ carthage update --platform iOS --use-xcframeworks
$ ./run_mix
$ % open Three.xcodeproj

Xcodeが起動するのでスタートボタンを押して実行してください

実機でハマったこと

  • iPhone上でデベロッパモード オン必須
    スクリーンショット 2025-12-04 11.26.15.jpeg

  • Teamの選択が必要
    image.png

  • 認証まわりで動かない時

    • Xcodeのクリーンビルド
    • Mac本体を再起動
    • iPhone再起動
    • その後 アプリを認証

全てを一度にやって動きました

ソース

4
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?