2007年5月8日火曜日

3Dモデルの表示

3Dモデルの表示。
CODEZINEを参考にHSP3へ移植させていただきました。

参考




3Dモデルファイル :
v 0.0 1.0 0.0
v 0.7 -1.0 0.7
v 1.0 -1.0 0.0
v 0.7 -1.0 -0.7
v 0.0 -1.0 -1.0
v -0.7 -1.0 -0.7
v -1.0 -1.0 0.0
v -0.7 -1.0 0.7
v 0.0 -1.0 1.0
v 0.0 -1.0 0.0
f 1 2 3
f 1 3 4
f 1 4 5
f 1 5 6
f 1 6 7
f 1 7 8
f 1 8 9
f 1 9 2
f 10 3 2
f 10 4 3
f 10 5 4
f 10 6 5
f 10 7 6
f 10 8 7
f 10 9 8
f 10 2 9
#include "hspda.as"
#include "gdi32.as"

#const global NUM_OF_VERTICES   6       // 頂点の数
#const global NUM_OF_FACES      8       // 面の数
#const global PI                3.14159 // 円周率

// **[↓VertexModule]*************************************************

#module VertexModule x, y, z
// 頂点の登録
#modinit double d1, double d2, double d3
    x = d1 : y = d2 : z = d3
    return

#modfunc _getRotX
    return x*cos(theta) + z*sin(theta)
#modfunc _getRotY
    return x * sin(phi) * sin(theta) + y * cos(phi) - z * sin(phi) * cos(theta)
#modfunc _getRotZ
    return -x * cos(phi) * sin(theta) + y * sin(phi) + z * cos(phi) * cos(theta)
// 回転後の頂点座標を返す関数群
#defcfunc getRotX var modVar
    _getRotX modVar : return refdval
#defcfunc getRotY var modVar
    _getRotY modVar : return refdval
#defcfunc getRotZ var modVar
    _getRotZ modVar : return refdval
// 画面上の頂点座標を返す関数群
#defcfunc getScreenX var modVar
    _getRotX modVar : return intrefdval * scale + centerX)
#defcfunc getScreenY var modVar
    _getRotY modVar : return int(-refdval * scale + centerY)

// 角度・倍率を設定
#define global setArgs(%1 = 0%2 = 0) _setArgs %1%2
#define global setScale(%1 = 0) _setScale %1
#deffunc _setArgs double d1, double d2
    phi = limitf(d2, -PI/2PI/2)
    theta = d1
    return
#deffunc _setScale double d1
    if d1 {
        scale = d1
    } else {
        scale = 0.8 * ginfo_winx / 2
    }
    return

// 角度・倍率に加算
#deffunc addArgs double d1, double d2
    phi = limitf(phi + d2, -PI/2PI/2)
    theta += d1
    return
#deffunc addScale double d1
    scale = limitf(scale + d1, 10.0200)
    return

// 中心座標と角度・倍率の初期化
#deffunc vInit
    centerX = ginfo_winx / 2
    centerY = ginfo_winy / 2
    setArgs : setScale
    return
#global

// **[end of VertexModule]********************************************

// **[↓FaceModule]***************************************************

#module FaceModule vertex, z, nx, ny, nz
// 面を登録
#modinit var v1, var v2, var v3
    vertex = v1, v2, v3
    return

// 面の重心の奥行き と 法線ベクトルを再計算
#modfunc renewData
    z = getRotZ(vertex(0)) + getRotZ(vertex(1)) + getRotZ(vertex(2))

    v1_v0_x = getRotX(vertex(1)) - getRotX(vertex(0))
    v1_v0_y = getRotY(vertex(1)) - getRotY(vertex(0))
    v1_v0_z = getRotZ(vertex(1)) - getRotZ(vertex(0))
    v2_v0_x = getRotX(vertex(2)) - getRotX(vertex(0))
    v2_v0_y = getRotY(vertex(2)) - getRotY(vertex(0))
    v2_v0_z = getRotZ(vertex(2)) - getRotZ(vertex(0))

    nx = v1_v0_y * v2_v0_z - v1_v0_z * v2_v0_y
    ny = v1_v0_z * v2_v0_x - v1_v0_x * v2_v0_z
    nz = v1_v0_x * v2_v0_y - v1_v0_y * v2_v0_x

    len = sqrt(nx*nx + ny*ny + nz*nz)
    nx /= len : ny /= len : nz /= len
    return

// i5番目の辺の画面上の座標を返す
#modfunc getLine var v1, var v2, var v3, var v4, int i5
    v1 = getScreenX(vertex(i5))
    v2 = getScreenY(vertex(i5))
    v3 = getScreenX(vertex((i5 + 1) \ 3))
    v4 = getScreenY(vertex((i5 + 1) \ 3))
    return

// 面を構成する頂点の画面上座標を配列に格納して返す
#modfunc getPoints array v1
    repeat 3
        v1(cnt * 2) = getScreenX(vertex(cnt)), getScreenY(vertex(cnt))
    loop
    return

// 重心のZ座標を返す関数
#modfunc _getZofFace
    return z
#defcfunc getZofFace var modVar
    _getZofFace modVar : return refdval

// 法線ベクトルのZ座標を返す関数
#modfunc _getNormalZofFace
    return nz
#defcfunc getNormalZofFace var modVar
    _getNormalZofFace modVar : return refdval
#global

// **[end of FaceModule]**********************************************

// **[↓Draw3dModule]*************************************************

#module Draw3dModule
// 初期化(各種変数の準備)
#deffunc init3d
    vInit
    CreatePen 010x000000
    if stat == 0 : dialog "CreatePenが失敗しました"1 : end
    hPen = stat
    SelectObject hDC, hPen

    dim arr,    NUM_OF_FACES
    dim iPoint, NUM_OF_VERTICES
    return

#deffunc drawModel array vertices, array faces, local x0, local x1, local y0, local y1
    i = 0
    foreach(faces)
        renewData faces(cnt)
        arr(i)  = int(getZofFace(faces(cnt)) * 1000)         // sortvalが実数を扱えないため、整数に変換
        i++
    loop

    color
    sortval@ arr, 0
    foreach(arr)
        sortget@ i, cnt
        // 塗りつぶし&線の描画
        nz = getNormalZofFace(faces(i))
        if nz < 0 : continue

        getPoints faces(i), iPoint

        hsvcolor 76128, nz * 255
        CreateSolidBrush ginfo_r | (ginfo_g << 8) | (ginfo_b << 16)
        if stat == 0 : dialog "CreateSolidBrushが失敗しました"1 : end
        hBrush = stat
        SelectObject hDC, hBrush
        Polygon hDCvarptr(iPoint), 3
        DeleteObject hBrush
    loop
    return

#deffunc _creanUp onexit
    DeleteObject hPen
    return
#global

// **[end of Draw3dModule]********************************************

// **[↓メインスクリプト]*********************************************

    // 各種初期化
    screen 0300300
    init3d                              // モジュールの初期化
    title "3Dモデルを表示する"

    dimtype vertices, 5NUM_OF_VERTICES// 頂点(モジュール変数)を格納する配列を宣言
    dimtype faces,    5NUM_OF_FACES   // 面(モジュール変数)を格納する配列を宣言

    gosub *set                          // 点と面の登録
    needToDraw = 1                      // 描画フラグ:まず最初は描画する必要がある

*main
    gosub *drag
    gosub *draw
    await 10
    goto *main

*drag
    stick key, 2561
    if key & 256 {
        if draging {
            getkey shiftIsPushed, 16
            if shiftIsPushed {          // Shiftを押した状態では倍率の変更
                addScale 1.0 * (ginfo(1) - logY)
            } else {                    // 押していない時は回転
                addArgs 0.01 * (ginfo(0) - logX), 0.01 * (ginfo(1) - logY)
            }
        } else {
            needToDraw = 1
            draging = 1
        }
        logX = ginfo(0) : logY = ginfo(1)
    } else {
        draging = 0
    }
    return

*draw
    if needToDraw {
        // 描画する必要があるときのみ実行
        redraw 0
        color 255255255 : boxf
        drawModel vertices, faces
        redraw 1
        if draging == 0 : needToDraw = 0
    }
    return

*set
    dialog "txt"16"3Dモデルデータ(wavefront objフォーマット)"
    if stat == 0 : end
    fileName = refstr
    notesel data
    noteload fileName
    sdim prm, 163

    repeat notemax
        noteget sLine, cnt
        i = 2
        repeat 3
            getstr prm(cnt), sLine, i, ' '
            i += strsize
        loop
        if peek(sLine, 0) == 'v' {
            newmod vertices, vertexModule, double(prm(0)), double(prm(1)), double(prm(2))
            continue
        }
        if peek(sLine, 0) == 'f' {
            newmod faces, faceModule, vertices(int(prm(0))-1), vertices(int(prm(1))-1), vertices(int(prm(2))-1)
            continue
        }
    loop
    sdim data, 4
    return

0 件のコメント: