0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ARのSNS作ってみる オブジェクト作成編

Last updated at Posted at 2024-10-20

初めに

 本記事は以下の記事の続きになります。まだ読まれていない方は読まれてから本記事を読むと分かり易いと思います。

 前回は投稿したい内容を入力する画面と、タップしたらオブジェクトが描写できるところまで解説しました。本記事では以下2点について解説していきます。

  • 入力された内容が記載されたオブジェクトを描写できる
  • 入力された内容(文字列)の長さによってオブジェクトを伸び縮みできる

入力された内容が記載されたオブジェクトを描写する

 入力される内容は日本語の文字列です。ひらがな、カタカナ、漢字が存在するため、文字をオブジェクト化して描写するというのは難しいです。したがって文字列を画像化してオブジェクトにテクスチャとして張り付けるという方法を取ります。コードは以下です。

import android.graphics.Bitmap
import android.graphics.Color
import android.graphics.Canvas
import android.graphics.Paint.Align
import android.graphics.Paint
import android.graphics.Typeface

class MessageTextBitmap (messageText: String) {

    val messageTextBitmap: Bitmap = textToBitmap(messageText, 30f, Color.WHITE)

    private fun textToBitmap(text: String, textSize: Float, textColor: Int): Bitmap {
        val paint = Paint(Paint.ANTI_ALIAS_FLAG)
        paint.textSize  = textSize
        paint.color     = textColor
        paint.textAlign = Align.LEFT
        paint.typeface  = Typeface.DEFAULT_BOLD

        val testList: List<String> =  text.split("\n")

        val longestText = testList.reduce { longestText, text -> if (text.length > longestText.length) text else longestText }
        val width       = (paint.measureText(longestText) + 50f).toInt()
        val height      = ((-paint.ascent() + paint.descent() + 5f) * testList.size + 50f).toInt()

        val image = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
        val canvas = Canvas(image)

        canvas.drawColor(Color.CYAN)

        for (i in testList.indices) {
            val x = (width - paint.measureText(longestText)) / 2f
            val y =
                if (i == 0) {
                    -paint.ascent() + (50f / 2f)
                } else {
                    -paint.ascent() + (50f / 2f) + ((-paint.ascent() + paint.descent() + 5f) * (i))
                }

            canvas.drawText(testList[i], x, y, paint)
        }

        return image
    }
}

 テキストを受け取ったら文字数、行数に応じて画像のサイズを動的に設定します。実行すると以下のような画像ができます。

タイトルなし.png

 次にテクスチャを張り付けるベースとなるオブジェクトを作成します。右がオブジェクトの形で左がテクスチャ座標です。テクスチャ座標は0~1の相対距離で計算されます。オブジェクトの表面のテクスチャ座標はテクスチャの全体を張り付けられるようにして、オブジェクトの裏面のテクスチャ座標はすべて0,0座標に置いてテクスチャの端の色が適用されるだけにしています。

image.png

 このオブジェクトに先ほどの画像をテクスチャとして貼り付けると入力された内容が記載されたオブジェクトを描写できます。

Screenshot_20241020-150957.png

 しかし今のままではオブジェクトの大きさが固定なので入力された文字数が長かったり、改行があった場合、画像がオブジェクトのサイズにスケールされてしまいます。

Screenshot_20241020-151102.png

入力された内容(文字列)の長さによってオブジェクトを伸び縮みする

 次に画像サイズに合わせてオブジェクトのサイズも動的に変更します。まずは全角の一文字が丁度よく収まるサイズのオブジェクトを作成します。

image.png

 次にこのオブジェクトを入力された文字列の画像サイズに合わせて動的に大きさを変えていきます。objファイルはテキストベースで情報を持っています。なのでテキストファイルとして読み込み、書き換えて再度保存するという方法を取ります。
 利用したソフトによりますがobjファイルは頂点座標の情報を「v {x} {z} {y}」の形で持っています。この{x}と{y}を変えることでオブジェクトをX軸Y軸に伸び縮みさせることができます。objファイルは頂点座標の他にもテクスチャ座標の情報なども持っていますが、テクスチャ座標は上述した通り相対距離で持っているため、オブジェクトを伸び縮みさせるだけなら何も変えなくて大丈夫です。
 objファイルを書き換えるコードは以下の通りです。

import android.content.Context
import android.content.res.AssetManager
import java.io.File
import java.io.InputStream
import kotlin.text.split
import kotlin.text.startsWith
import kotlin.text.toFloat

class MessageTextObject (private val messageTextBitmap: MessageTextBitmap, private val assetManager: AssetManager, private val context: Context) {

   private val baseImageWidth: Double  = 50.0
   private val baseImageHeight: Double = 60.0

   fun convertObjFile() {
       val imageWidth  = messageTextBitmap.messageTextBitmap.width
       val imageHeight = messageTextBitmap.messageTextBitmap.height

       val ratioWidth: Double  = imageWidth.toDouble() / baseImageWidth
       val ratioHeight: Double = imageHeight.toDouble() / baseImageHeight

       val objFile = assetManager.open( "models/message.obj")
       val newObjFileLine = multiplyVertices(objFile, ratioWidth, ratioHeight)

       val newObjFile = File(context.filesDir, "message.obj")
       newObjFile.writeText(newObjFileLine)
   }

   private fun multiplyVertices(objFile: InputStream, xMultiplier: Double = 1.0, yMultiplier: Double = 1.0, zMultiplier: Double = 1.0): String {
       val lines = objFile.bufferedReader().readLines()
       val newLines = mutableListOf<String>()

       for (line in lines) {
           if (line.startsWith("v ")) {
               val parts = line.split(" ")
               if (parts.size >= 4) {
                   val x = parts[1].toFloat() * xMultiplier
                   val z = parts[2].toFloat() * zMultiplier
                   val y = parts[3].toFloat() * yMultiplier
                   newLines.add("v $x $z $y")
               } else {
                   newLines.add(line)
               }
           } else {
               newLines.add(line)
           }
       }

       return newLines.joinToString("\n")
   }
}

 全角一文字の時の画像サイズが50×60なので、それをベースサイズとして、入力された文字列の画像がベースサイズから縦横それぞれ何倍の大きさになっているかを求めます。求められた倍率をobjファイルの頂点座標のX、Yにそれぞれ乗算します。これによってオブジェクトが動的に大きさを合わせてくれます。

Screenshot_20241020-144810.png

Screenshot_20241020-145000.png

まとめ

 今回はここまでにします。次回はオブジェクトの絶対位置取得や永続化まで終わらせて一旦必須機能はすべて実装しようかなと思います。細かなレイアウトやユーザ登録機能、オブジェクトの形や色もユーザが任意で変えられるような機能も追加したいので、完成はもう少しかかりそうです。

0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?