配列にはいくつか種類があるらしい。
・[T;N] ... スタック上に固定長配列
・Vec<T> ヒープ上の可変長配列
・Box<[T;N]>ヒープ上に固定長配列
注意点として、基本的に配列の要素は全て初期化しなければならない。可変長配列でcapacityを指定できるVecはともかく、それ以外をメモリ確保だけして初期化したくない場合には、std::mem::MaybeUninitを使わなければならない。これはunsafeで囲まなければならない。
fn main() { //////////////////////////////////////////// // 固定長配列(スタック)
let mut ary1:[i32;5]=[1,2,3,4,5]; for a1 in ary1{ print!("{} ",a1); } println!("");
// 初期化しないバージョン let mut ary2:[i32;5] = unsafe { std::mem::MaybeUninit::uninit().assume_init() }; for a2 in ary2{ println!("* {} ",a2); } println!("\n");
//////////////////////////////////////////// // 可変長配列
let mut vec1:Vec<&str> = vec!["a", "b", "c"]; vec1.push("d"); for v1 in vec1{ print!("{} ",v1); } println!("");
// 初期化しないバージョン let mut vec2:Vec<&str> = vec![]; vec2.push("d"); for v2 in vec2{ print!("* {} ",v2); } println!("\n");
//////////////////////////////////////////// // 固定長配列(ヒープ)
let mut box1:Box<[f32;5]> = Box::new([10.5 , 11.5 , 12.5 , 13.5 , 14.5]); for b1 in box1.iter(){ print!("{} ",b1); } println!("");
// 初期化しないバージョン let mut box2:Box<[f32;5]> = Box::new(unsafe { std::mem::MaybeUninit::uninit().assume_init() } ); for b2 in box2.iter(){ println!("* {} ",b2); }
}
初期化しなければ不正な値が入っていることを確認できた。
1 2 3 4 5
* 112
* 0
* -580595040
* 367
* -580614544
a b c d
* d
10.5 11.5 12.5 13.5 14.5
* 2
* 0
* -1030514000000000000
* 0.000000000000000000000000000000000000000000514
* 0.000000000000000000000000000000000000000000006
構造体関連で、struct,impl,trait の役割は以下(だと考えられる)
本当はポリモーフィズムをやりたかったがどうしても理解が追い付かなかったので省略。
// #[derive(Debug)] をつけるとprintln!でそのまま表示できるようになる #[derive(Debug)] // 構造体の定義。 struct Rectangle{ width:i32, height:i32, }
fn main() { // 初期化なしでは定義できない //let mut rr = Rectangle{}; let mut rr = Rectangle{width:0,height:0}; // mut をつけているので代入できる rr.width = 20; rr.height = 20; // #[derive(Debug)] をつけているので {:?} で表示できる println!("{:?}",rr); }
// 構造体の定義 struct Rectangle{ width:i32, height:i32, }
// implの中でメソッドや関連関数を定義 impl Rectangle{ // Rectangleのメソッドを定義 メソッドはselfをとる。 selfがないなら関連関数 fn area(&self)->i32{ return self.width * self.height; } }
fn main() { // インスタンス生成 let mut rr = Rectangle{width:0,height:0}; rr.width = 20; rr.height = 20; // メソッド呼び出し println!("{}",rr.area() ); }
implの中に&selfをとらない関数を定義すると関連関数になる。
新規インスタンスを作成する関連関数を定義する場合、名前をnewにするのが一般的らしい。
呼び出しは 構造体名::関連関数名() となる。
// 構造体の定義 struct Rectangle{ width:i32, height:i32, } impl Rectangle{ // 関連関数。 &selfをとらない。 // Rectangle::new で呼び出す。 // 新規インスタンス生成関連関数は「new」にする習慣がある fn new()->Rectangle{ return Rectangle{width:20,height:30}; } // メソッド fn area(&self)->i32{return self.width * self.height;} }
fn main() { // 関連関数でインスタンス生成 let mut rr = Rectangle::new(); println!("{}",rr.area() ); }
implをbehavior_calc_areaの実装とすることで、areaメソッドの実装を強制することができる。
// 振舞いの定義 trait behavior_calc_area{ fn area(&self)->f32; }
// 構造体の定義 struct Rectangle{ width:i32, height:i32, } // トレイトのR実装 impl behavior_calc_area for Rectangle{ // メソッド fn area(&self)->f32{ return (self.width * self.height) as f32; } }
struct Triangle{ width:i32, height:i32, } // トレイトのR実装 impl behavior_calc_area for Triangle{ // メソッド fn area(&self)->f32{ return (self.width * self.height) as f32 / 2.0 ; } }
fn main() { // インスタンス生成 let t:Triangle = Triangle{width:10,height:5}; let r:Rectangle = Rectangle{width:10,height:5}; println!("{}\n",t.area() ); println!("{}\n",r.area() ); }
基本はそんなに複雑ではないのだが、呼び出すときに暗黙型変換してくれないらしく、明示的型変換(as f32) を入れないといけないらしい。
// 関数定義 // 値渡し fn add(a:f32,b:f32)->f32{ return a+b; } fn main() { // 関数呼び出し // 引数に整数を入れてもaddのf32に暗黙変換できないらしいので // as f32 で実数に型変換する let k = add(3 as f32,5 as f32); println!("k= {}",k); }
不変参照で渡すときは渡す側と受ける側で&をつける。
// デフォルトで不変参照。C++でいうところのconst。kの内容を変更できない fn add2(k: &i32)->f64{ // &mutでないときは *k としなくていいらしい return (k + 2) as f64; }
fn main() { let a:i32=3; // mutがついていないので変更不可 println!(" {}", add2(&a) ); // aの定義時にmutをつけていないのでここもただの& }
可変参照は型名の前に&mut をつける。
呼び出す側でも&mutが必要。
さらにこの値を使うときは参照外しとして*をつけなければならない。もっとほかの記号はなかったのか。
// &mut で可変参照 fn swap(i:&mut i32,j:&mut i32){ let k:i32; // &mutの時は頭に*をつけて *i とする k = *i; *i = *j; *j = k; }
fn main() { let mut a:i32=3; let mut b:i32=8; swap(&mut a,&mut b); println!("{} {}",a,b); }
C++のテンプレートのようなgenericsという機能があるが、ただ<T>としただけでは動かない。printlnすらできない。いずれ詳しく調べる。
https://www.koderhq.com/tutorial/rust/generics/
use std::ops::Mul; fn square<T: Mul<Output = T> + Copy >(val:T)->T{ return val*val; }
use std::fmt::Display; fn console_log<T: Display> (x: T) { println!("{}", x); }
fn main() { println!("{}", square(3) ); console_log( 3 ); }
fn main() { for x in 1..=9{ for y in 1..=9{ print!("{} ",x*y); } println!(""); } }
なんとRustのforにはCのforのようにカウンタを使う記述法がない。その手のものが必要な時はwhileを使う。さらにRustにはインクリメント演算子++がない。+=はあるのでそれを使う。
なぜ++がないのかよくわからないが、コンパイラが進化した今inc命令とadd命令をプログラマが区別する必要はないのでそのために言語仕様を複雑にする必要はないとかそんな感じではないだろうか(適当)。
fn main() { let mut k = 0;// 変数はデフォルトで変更不可なのでmutをつける while k < 10{ println!("k=={}",k); k+=1; // Rustには++がない } }
ifはCとかなり近い。ただし()で囲う必要がない。
fn main() { let k = 3; if k == 1{ println!("k==1"); } else if k==2{ println!("k==2"); } else{ println!("other k"); } }
特に違うものとして、Cの三項演算子に該当するものがifで表記するようになっている。
fn main() { let k = 2; let msg = if k%2!=0 {"odd"}else{"even"}; println!("{}",msg); }
いろいろとCのswitchに近いがbreakしないところが特に違う。あとカンマ区切り。
fn main() { let i = 2; // switchに近い。 // ただし、matchの中で、「取りうるすべての可能性について」処理する必要がある match i{ // Cと違いbreakしなくてもいい。ほかの式が実行されることはない 3 => println!("three"), // 複数の指揮を実行するときは{}で括る 4 => { println!("four"); println!("FOUR") }, // 取りうるすべての可能性を記述しなければいけないため // ワイルドカード _ を使ってCのdefaultと同じことをする。 _ => println!("others"), }; }
matchの結果をそのまま変数に渡すこともできる。
fn main() { let i = 3; let msg:&str = match i{ 3 => "three", 4 => "four", _ => "others", }; println!("{}",msg); }
// 入出力を使う use std::io; fn main() { // 変数 buffer を定義 let mut buffer = String::new(); // bufferへ文字列入力 io::stdin().read_line(&mut buffer); // bufferを表示 println!("{}",buffer); }
// 入出力を使う use std::io; fn main() { // 変数 buffer を定義 let mut buffer = String::new(); io::stdin().read_line(&mut buffer); //標準入力から入力 // 型指定 :i32 が必要 // read_lineから読み込んだ文字列に対してはtrim()が必要 let val:i32 = buffer.trim().parse().unwrap(); // bufferを表示 println!("{}",val); }
// ファイル入力を使う use std::{fs::File, io::Read}; fn main() { let filename = "test.txt";
// ファイル入力を使う let mut file = File::open(filename).expect("file not found"); // 変数 buffer を定義 let mut buffer = String::new(); // ファイルから入力 file.read_to_string(&mut buffer).expect("something went wrong reading the file"); // ファイルを閉じる方法 // 方法1 スコープを抜けるとdropが呼ばれる(C++のデストラクタのようなもの) // 方法2 明示的にdropを呼び出す drop(file); // 明示的にdropを呼び出す // bufferを表示 println!("{}",buffer); }
use std::fs::File; // write!やwrite_allを使うのに必要 use std::io::Write; // 戻り値は std::io::Result<()> 型 // 最後に OK(()) を返す。 // これを入れないとwrite_allなどが使えない fn main()-> std::io::Result<()> { let filename = "test.txt"; // ファイル入力を使う let mut file = File::create(filename)?; // 末尾の ? は例外処理のようなもの関連 // ただしRustには例外がないのであくまでそれっぽく振る舞うだけ。 // 変数 buffer を定義 let _buffer = "hello".to_string(); // ファイルへ書き出し file.write_all(_buffer.as_bytes())?; // こっちも使える // write!(file,"hello2")?; // ファイルを閉じる drop(file); // std::io::Result<()> 型の場合 Ok(()) }
CargoはRustのビルドシステム及びパッケージマネージャ。
cargo buildはカレントディレクトリにsrcディレクトリがあることを想定する。
デバッグビルド
リリースビルド
Rustではパッケージのことを「クレート」というらしい。Cargo.tomlの[dependencies]にモジュール名とバージョンを記述する。
[package]
name = "myproj"
version = "0.1.0"
edition = "2021"
[dependencies]
regex = "1.6.0"
use regex::Regex; fn main() { let re = Regex::new(r"^\d{4}-\d{2}-\d{2}$").unwrap(); println!("Did our date match? {}", re.is_match("2014-01-01")); }
cargo buildすると勝手にcrates.ioからクレートを取得するので上記の記述以外にすることはない。
VSCodeでデバッグできるのだが、私の環境では、クレートを使っているとVSCode関連で失敗する。
細かく検証していないが上記regexを使った場合VSCodeからは実行すらできない。PowerShellからは動く。
どれだけ検索しても対策が見つからなかったがcodelldbのバージョンによっては起こる?らしい。
プログラミング言語Rustをやってみる。
Windowsでは、
https://learn.microsoft.com/ja-jp/windows/dev-environment/rust/setup
のいうとおりにやればビルドできるようになる。以下要約。
1.VC++をインストール(必須環境)
2.Rustをインストール(本体)
https://www.rust-lang.org/tools/install
3.VSCodeをインストール(エディタ・開発環境)
https://code.visualstudio.com/
4.VSCodeの拡張機能をインストール
rust-analyzer
CodeLLDB
プロジェクト生成にはGUIがないのでコマンドラインからcargoコマンドを叩く。
まずはプロジェクトを作成するディレクトリへ行き、
cargoコマンドでプロジェクトを作成する。「cargo new プロジェクト名」
作成したプロジェクトのディレクトリを引数にVSCodeを起動するか、VSCodeを開きディレクトリをドラッグ&ドロップする。
コマンドラインから開く場合(VSCodeのパスが通っている場合)
code first_rust_project
VSCodeにD&Dしてもいい
src\main.rs ... ソースコード本体
Cargo.toml ... プロジェクトの設定ファイル
VSCodeで、 [実行]>[デバッグの開始] 。
初回のみエラーメッセージのようなものが出るが、「OK」「はい」として、launch.jsonが出たら何もせず閉じてもう一度デバッグの開始。
以下引用:
CodeLLDB 拡張機能とデバッガーで初めてアプリを実行すると、"起動構成が提供されていないため、デバッグを開始できません" というダイアログ ボックスが表示されます。 [OK] をクリックすると、"このワークスペースで Cargo.toml が検出されました。 ターゲットの起動構成を生成しますか?" という 2 番目のダイアログ ボックスが表示されます。 [はい] をクリックします。 次に、launch.json ファイルを閉じて、もう一度デバッグを開始します。
近い点をより大きく描画することで頂点がカメラに近づく表現をする。
#pragma warning(disable:4996) #pragma comment(lib,"glew32.lib") #include <gl/glew.h> #include <GL/glut.h> #include <fstream> #include <sstream> #include <vector> #include <algorithm> #include <array> typedef std::array<GLfloat,3> point_t; //モデルの座標用 x y z typedef std::array<GLfloat,4> color_t;//テクスチャ座標用 r g b a ////////////////////////////////////////// ////////////////////////////////////////// //////////////////////////////////////////
const char* vshader_program = R"( #version 460 core layout(location = 0) in vec3 vtx; layout(location = 1) in vec4 color; out vec4 vColor; uniform mat4 ModelViewMatrix; uniform mat4 ProjectionMatrix; // カメラ座標 uniform vec4 campos; void main() { gl_Position = ProjectionMatrix * ModelViewMatrix * vec4(vtx, 1.0); // 近いものほど大きく見せる float size = 50 / distance(campos,gl_Position); vColor = color; gl_PointSize = size; // 頂点のサイズ(ピクセル) } )";
////////////////////////////////////////// ////////////////////////////////////////// //////////////////////////////////////////
const char* fshader_program = R"( #version 460 core out vec4 FragColor; in vec4 vColor; void main() { FragColor = vColor; } )";
///////////////////////////////// //バッファとデータ GLuint vertexbufferName; GLuint colorbufferName; const GLuint v_count=100; GLuint VertexShaderID; void prepare_vertex_shader(); GLuint FragmentShaderID; void prepare_fragment_shader(); GLuint ProgramID; void link_program(); void init(void); void display(void); void timer(int value) { // 再描画 glutPostRedisplay(); // 100ミリ秒後にtimer関数を引数0で自分自身を呼び出す glutTimerFunc(100, timer, 0); }
int main(int argc, char* argv[]) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_RGBA); glutCreateWindow(argv[0]); glutDisplayFunc(display); glutReshapeWindow(300, 300); glewInit(); // glewの初期化 init(); glutTimerFunc(100, timer, 0); glutMainLoop(); return 0; } void prepare_buffers(); void init(void) { //頂点データと色情報の作成 prepare_buffers(); //頂点シェーダの準備 prepare_vertex_shader(); //フラグメントシェーダの準備 prepare_fragment_shader(); //プログラムのリンク link_program(); } //////////////////////////////////////////////////
void prepare_buffers() { ////////////////////////////////////////// ////////////////////////////////////////// std::vector< point_t > vertex; std::vector< color_t > color; float scale=50000.0f; for (size_t i = 0; i < v_count; i++){ float x = (rand() % (int)scale) * (1.0f / scale) - 0.5; float y = (rand() % (int)scale) * (1.0f / scale) - 0.5; float z = (rand() % (int)scale) * (1.0f / scale) - 0.5; vertex.emplace_back(point_t{ x*1.5f,y*1.5f,z*50 }); float r = (rand() % (int)scale) * (1.0f / scale); float g = (rand() % (int)scale) * (1.0f / scale); float b = (rand() % (int)scale) * (1.0f / scale); color.emplace_back(color_t{ r,g,b,1 }); } ////////////////////////////////////////// ////////////////////////////////////////// glGenBuffers(1, &vertexbufferName); glBindBuffer(GL_ARRAY_BUFFER, vertexbufferName); glBufferData(GL_ARRAY_BUFFER, 3 * v_count * sizeof(GLfloat), vertex.data(), GL_STATIC_DRAW); glGenBuffers(1, &colorbufferName); glBindBuffer(GL_ARRAY_BUFFER, colorbufferName); glBufferData(GL_ARRAY_BUFFER, 4 * v_count * sizeof(GLfloat), color.data(), GL_STATIC_DRAW); }
void prepare_vertex_shader() { ////////////////////////////////////////////// // シェーダを作ります // (作るといっても宣言みたいなもの) VertexShaderID = glCreateShader(GL_VERTEX_SHADER); //////////////////////////////////////////// // ファイルから頂点シェーダを読み込みます。 // 注意 ここはSTLのifstreamとかstringstreamの使い方の話で、 // OpenGL命令は一つも無い。 std::string VertexShaderCode = vshader_program; //////////////////////////////////////////// // 頂点シェーダをコンパイルします。 //printf("Compiling shader : %s\n", vertex_file_path); char const* VertexSourcePointer = VertexShaderCode.c_str(); glShaderSource(VertexShaderID, 1, &VertexSourcePointer, NULL); glCompileShader(VertexShaderID); //////////////////////////////////////////// // エラーチェック GLint Result = GL_FALSE; int InfoLogLength; // 頂点シェーダをチェックします。 glGetShaderiv(VertexShaderID, GL_COMPILE_STATUS, &Result); glGetShaderiv(VertexShaderID, GL_INFO_LOG_LENGTH, &InfoLogLength); if (Result == FALSE) { std::vector<char> VertexShaderErrorMessage(InfoLogLength); glGetShaderInfoLog(VertexShaderID, InfoLogLength, NULL, &VertexShaderErrorMessage[0]); fprintf(stdout, "%s\n", &VertexShaderErrorMessage[0]); } }
void prepare_fragment_shader() { ///////////////////////////////////////////// // シェーダを作ります。 FragmentShaderID = glCreateShader(GL_FRAGMENT_SHADER); ///////////////////////////////////////////// // ファイルからフラグメントシェーダを読み込みます。 std::string FragmentShaderCode = fshader_program; ///////////////////////////////////////////// // フラグメントシェーダをコンパイルします。 //printf("Compiling shader : %s\n", fragment_file_path); char const* FragmentSourcePointer = FragmentShaderCode.c_str(); glShaderSource(FragmentShaderID, 1, &FragmentSourcePointer, NULL); glCompileShader(FragmentShaderID); GLint Result = GL_FALSE; int InfoLogLength; ///////////////////////////////////////////// // フラグメントシェーダをチェックします。 glGetShaderiv(FragmentShaderID, GL_COMPILE_STATUS, &Result); glGetShaderiv(FragmentShaderID, GL_INFO_LOG_LENGTH, &InfoLogLength); if (Result == GL_FALSE) { std::vector<char> FragmentShaderErrorMessage(InfoLogLength); glGetShaderInfoLog(FragmentShaderID, InfoLogLength, NULL, &FragmentShaderErrorMessage[0]); fprintf(stdout, "%s\n", &FragmentShaderErrorMessage[0]); } }
void link_program() { GLint Result = GL_FALSE; int InfoLogLength; //////////////////////////////////////// // プログラムをリンクします。 fprintf(stdout, "Linking program\n"); ProgramID = glCreateProgram(); glAttachShader(ProgramID, VertexShaderID); glAttachShader(ProgramID, FragmentShaderID); glLinkProgram(ProgramID); //////////////////////////////////////// // プログラムをチェックします。 glGetProgramiv(ProgramID, GL_LINK_STATUS, &Result); glGetProgramiv(ProgramID, GL_INFO_LOG_LENGTH, &InfoLogLength); std::vector<char> ProgramErrorMessage((std::max)(InfoLogLength, int(1))); glGetProgramInfoLog(ProgramID, InfoLogLength, NULL, &ProgramErrorMessage[0]); fprintf(stdout, "%s\n", &ProgramErrorMessage[0]); }
void display(void) { glClearColor(0, 0, 0, 1); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glEnable(GL_DEPTH_TEST); glLoadIdentity(); //glTranslated(0.5, 0.0, 0.0);//平行移動 glUseProgram(ProgramID); GLfloat mfproj[16]; GLfloat mftran[16]; mfproj[0] = 2.26961231; mfproj[1] = 0.0; mfproj[2] = 0.0; mfproj[3] = 0.0; mfproj[4] = 0.0; mfproj[5] = 2.41421366; mfproj[6] = 0.0; mfproj[7] = 0.0; mfproj[8] = 0.0; mfproj[9] = 0.0; mfproj[10] = -1.00020003; mfproj[11] = -1.00000000; mfproj[12] = 0.0; mfproj[13] = 0.0; mfproj[14] = -0.0200020000; mfproj[15] = 0.0; static float z = -10.0; z += 0.2; mftran[0] = 1.0; mftran[1] = 0.0; mftran[2] = 0.0; mftran[3] = 0.0; mftran[4] = 0.0; mftran[5] = 1.0; mftran[6] = 0.0; mftran[7] = 0.0; mftran[8] = 0.0; mftran[9] = 0.0; mftran[10] = 1.0; mftran[11] = 0.0; mftran[12] = 0.0;//x mftran[13] = 0.0;//y mftran[14] = z;//z mftran[15] = 1.0; GLint proj = glGetUniformLocation(ProgramID, "ProjectionMatrix"); GLint modl = glGetUniformLocation(ProgramID, "ModelViewMatrix"); glUniformMatrix4fv(proj, 1, GL_FALSE, mfproj); glUniformMatrix4fv(modl, 1, GL_FALSE, mftran); GLint campos = glGetUniformLocation(ProgramID, "campos"); GLfloat fcp[4] = { 0,0,z }; glUniform3fv(campos,1, fcp); //gl_PointSizeを有効にする glEnable(GL_VERTEX_PROGRAM_POINT_SIZE); // 頂点バッファ glEnableVertexAttribArray(0); glBindBuffer(GL_ARRAY_BUFFER, vertexbufferName); glVertexAttribPointer( 0, // 属性0 3, // 1要素の要素数 x y z GL_FLOAT, // タイプ GL_FALSE, // 正規化しない(データが整数型の時) 0, // ストライド (void*)0 // 配列バッファオフセット ); // カラーバッファ glEnableVertexAttribArray(1); glBindBuffer(GL_ARRAY_BUFFER, colorbufferName); glVertexAttribPointer( 1, // 属性1 4, // 1要素の要素数 r g b a GL_FLOAT, // タイプ GL_FALSE, // 正規化しない(データが整数型の時) 0, // ストライド (void*)0 // 配列バッファオフセット ); // 5頂点を描く glDrawArrays(GL_POINTS, 0, v_count); glDisableVertexAttribArray(0); glDisableVertexAttribArray(1); glDisableVertexAttribArray(2); //////////////////////////////////////// //////////////////////////////////////// glFlush(); }
色々実験したいときに複雑なフォーマットだと読み込むのがいちいち大変なのでファイル形式を考えた。特徴は入力コードのコピペ量が少ないところ。
ファイル内のすべての行は「,」で区切られた4つの要素からなる
※ただし#が先頭の場合はスキップ
・ti 三角形の頂点index
・tn 三角形の面法線
・tc 三角形の色
・pp 頂点座標
・pc 頂点色
・pn 頂点法線
import bpy import sys def my_triangulation(target = bpy.context.object) : # なにも選択されていなければ終了 if target == None: return False # メッシュだけを対象とする if target.type == 'MESH' : # EDITモードへ移行するために現在のモードを保存して奥 current = bpy.context.object.mode # Editモードへ移行 bpy.ops.object.mode_set(mode = 'EDIT') # 三角形分割実行 bpy.ops.mesh.quads_convert_to_tris() # モードをリストア。これをしないとEDITモードのままで終わる。 bpy.ops.object.mode_set(mode = current) else: return False return True ############################################ ############################################ def to_simple_polygon_file(outputto) : obj = bpy.context.active_object mesh = obj.data pvecname = "points" # 頂点データの配列の変数名 vnvecname = "vnorms" # 頂点法線の配列の変数名 fvecname = "faces" # 面データの配列の変数名 fnvecname = "fnorms" # 面法線の配列の変数名 print("/////////////////////////////////////") print("#szlm,-,-,-,-", file = outputto) for f in mesh.polygons: if not fvecname is None : print( "ti,", f.vertices[0], ",", f.vertices[1], ",", f.vertices[2], 0, file = outputto ) if not fnvecname is None : print( "tn,", f.normal.x, ",", f.normal.y, ",", f.normal.z, 0, file = outputto ) for v in mesh.vertices: if not pvecname is None : print( "pp,", v.co.x, ",", v.co.y, ",", v.co.z, 0, file = outputto ) if not vnvecname is None : print( "pn,", v.normal.x, ",", v.normal.y, ",", v.normal.z, 0, file = outputto ) # 三角形分割 ret = my_triangulation() o = open('c:/data/myfile3.txt', 'w') to_simple_polygon_file(o) o.close() #as_cpp_array(sys.stdout)
#pragma once #pragma warning(disable:4996) #include <vector> #include <array> #include <type_traits> #include <string> /* #szlm,-,-,-,- ti,1,2,3,- tc,0.1,0.1,0.1,- tn,0.1,0.1,0.1,- pp,0.1,0.1,0.1,- pc,0.1,0.1,0.1,- pn,0.1,0.1,0.1,- */ struct szlm { std::vector<std::array<int, 4>> ti; std::vector<std::array<float, 4>> tc // triangle color ,tn// triangle normal ,pp// point position ,pc// point color ,pn// point normal ; }; //! @brief 簡易的メッシュフォーマットの読み込み //! @param [in] file ファイル名 //! @param [out] mesh メッシュを保存する構造体 bool read_mesh(const char* file, szlm* mesh) { FILE* fp = fopen(file, "r"); if (fp == NULL) return false; constexpr size_t DIM = 4; const size_t BUFFERSIZE = 2048; char b[BUFFERSIZE]; //,で分割 auto split = [](char* buffer, std::vector<std::string>& split)->void { const char* sp = ","; char* tok = strtok(buffer, sp); while (tok != NULL) { split.push_back(tok); tok = strtok(NULL, sp); /* 2回目以降 */ } }; //配列へ入力 while (fgets(b, BUFFERSIZE, fp) != NULL) { std::array<int, DIM> ii; std::array<float, DIM> ff; std::fill_n(ii.begin(), DIM, -1); std::fill_n(ff.begin(), DIM, INFINITY); std::vector<std::string> line; split(b, line);//入力行を,で分割 for (size_t i = 1; i < (std::min)(DIM, line.size()); i++) { ii[i - 1] = atoi(line[i].c_str()); ff[i - 1] = atof(line[i].c_str()); } if (b[0] == '#')continue; else if (strncmp(b, "ti", 2) == 0)mesh->ti.push_back({ ii[0],ii[1],ii[2],ii[3] });// triangle pointindex else if (strncmp(b, "tc", 2) == 0)mesh->tc.push_back({ ff[0],ff[1],ff[2],ff[3] });// triangle color else if (strncmp(b, "tn", 2) == 0)mesh->tn.push_back({ ff[0],ff[1],ff[2],ff[3] });// triangle normal else if (strncmp(b, "pp", 2) == 0)mesh->pp.push_back({ ff[0],ff[1],ff[2],ff[3] });// point position else if (strncmp(b, "pc", 2) == 0)mesh->pc.push_back({ ff[0],ff[1],ff[2],ff[3] });// point color else if (strncmp(b, "pn", 2) == 0)mesh->pn.push_back({ ff[0],ff[1],ff[2],ff[3] });// point normal } fclose(fp); return false; }
void disp(const szlm& mesh) { glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); glBegin(GL_TRIANGLES); for (size_t i = 0; i < mesh.ti.size(); i++) { int v0 = mesh.ti[i][0]; int v1 = mesh.ti[i][1]; int v2 = mesh.ti[i][2]; glNormal3fv(mesh.tn[i].data()); glVertex3fv(mesh.pp[v0].data()); glVertex3fv(mesh.pp[v1].data()); glVertex3fv(mesh.pp[v2].data()); } glEnd(); } int main() { struct szlm mesh; read_mesh("C:\\data\\myfile3.txt", &mesh); /* ... */ disp(mesh); }
オブジェクトをobject_3dから継承させ、intersectをoverrideする。それ以外はほぼ変わらない
#pragma once #include "Grid.hpp" #include <array> #include <vector> #include <memory>
////////////////////////////////////////////// //////////////////////////////////////////////
// 三次元オブジェクトのスーパークラス class object_3d { protected: glm::vec3 _color; public: object_3d(const glm::vec3& color_) :_color(color_) {} object_3d() {} virtual bool intersect( glm::vec3* pos_, glm::vec3* color_, glm::vec3* normal_, float* distance_, const std::array<glm::vec3, 2>& ray_)const = 0; const glm::vec3& color()const { return _color; } glm::vec3& color() { return _color; } };
// 球オブジェクト class object_sphere:public object_3d { glm::vec3 _center; float _r; public: object_sphere(glm::vec3 center_, glm::vec3 color_,float radius_) : _center(center_),_r(radius_), object_3d(color_) {} object_sphere() {} virtual bool intersect( glm::vec3* pos_, glm::vec3* color_, glm::vec3* normal_, float* distance_, const std::array<glm::vec3, 2>& ray_)const override; };
// 三角形オブジェクト class object_triangle :public object_3d { std::array<glm::vec3,3> _points; public: object_triangle( std::array<glm::vec3, 3> points_, glm::vec3 color_) : _points(points_), object_3d(color_){} object_triangle() {} bool intersect( glm::vec3* pos_, glm::vec3* color_, glm::vec3* normal_, float* distance_, const std::array<glm::vec3, 2>& ray_)const override; };
////////////////////////////////////////////// ////////////////////////////////////////////// // ポイントライト class light_point { glm::vec3 _position; glm::vec3 _diffuse_color; glm::vec3 _ambient_color; glm::vec3 _specular_color; public: light_point( const glm::vec3& position_, const glm::vec3& diffuse_color_, const glm::vec3& ambient_color_, const glm::vec3& specular_color_ ) : _position(position_), _diffuse_color(diffuse_color_), _ambient_color(ambient_color_), _specular_color(specular_color_) {} light_point() {} glm::vec3 calc_vector_point_to_light(const glm::vec3& point_)const { // from -> _position return glm::normalize(_position - point_); } const glm::vec3& position()const {return _position;} glm::vec3& position() { return _position; } const glm::vec3& diffuse_color()const {return _diffuse_color;} glm::vec3& diffuse_color() { return _diffuse_color; } const glm::vec3& ambient_color()const { return _ambient_color; } glm::vec3& ambient_color() { return _ambient_color; } const glm::vec3& specular_color()const { return _specular_color; } glm::vec3& specular_color() { return _specular_color; } }; struct hit_pixel_t { glm::vec3 _position; glm::vec3 _color; hit_pixel_t(const glm::vec3& position_, const glm::vec3& color_) : _position(position_), _color(color_) {} }; // レイキャスト用のクラス class my_raycast { grid g[2]; float _startZ; float _endZ; int _pxwidth; int _pxheight; public: glm::vec3 get_eye_position()const { float a = g[0].ywidth() / 2.0; float b = g[1].ywidth() / 2.0; float d = glm::length(g[1].center() - g[0].center()); float c = a * d / (b - a); // 始点 → 終点 glm::vec3 veceye = g[1].center() - g[0].center(); veceye = glm::normalize(veceye); return g[0].center() - veceye * c; } my_raycast() {} int width()const { return _pxwidth; } int height()const { return _pxheight; } // レイの開始点となるグリッド、終点となるグリッドを作成する void set(const float startZ_, const float endZ_, const int pxwidth_, const int pxheight_); //グリッド取得 const grid& get_grid(const size_t index)const { return g[index]; } //グリッドから作成するレイの数を取得 int ray_count() { return g[0].cellCount(); } //グリッドから作成するレイを取得 std::array<glm::vec3, 2> get_ray(const size_t index)const; //始点グリッド、終点グリッドの中間の位置を取得(表示用) float lookatZ()const { return (_startZ + _endZ) / 2.f; } ////////////////////////////////////////////// ////////////////////////////////////////////// private: // オブジェクト一覧 std::vector< std::shared_ptr<object_3d> > _objlist; std::vector< hit_pixel_t > _hits; public: void raycast(); std::vector< hit_pixel_t >& get_intersects() {return _hits;} void set_object(const object_sphere& obj_); void set_object(const object_triangle& obj_); ////////////////////////////// private: light_point _light; public: void set_light(const light_point& light_); const light_point& get_light()const { return _light; } }; // objectNormal 面法線 // lightDirection そのポイント→光源へのベクトル // lightColor ライトの色 inline glm::vec3 calc_diffuse( const glm::vec3& lightDiffuse, const glm::vec3& MaterialDiffuse, const glm::vec3& lightIncidence, const glm::vec3& objectNormal) { // 拡散反射は「そのポイント→光源」と面法線の内積を取る float sDotN = glm::max(glm::dot(-lightIncidence, objectNormal), 0.f); return lightDiffuse * MaterialDiffuse *sDotN; } inline glm::vec3 calc_ambient( const glm::vec3& LightAmbient, const glm::vec3& MaterialAmbient) { return LightAmbient * MaterialAmbient; } // https://araramistudio.jimdo.com/2017/10/02/%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0-directx-11%E3%81%A7%E9%8F%A1%E9%9D%A2%E5%8F%8D%E5%B0%84-specular-reflection/ // https://amengol.github.io/game_physics/using-glm-reflect-to-react/ // https://learnopengl.com/Lighting/Basic-Lighting inline glm::vec3 calc_specular( const glm::vec3& eyePosition, const glm::vec3& lightSpecularColor, const glm::vec3& materialSpecularColor, const glm::vec3& lightIncidence, // 入射 const float materialShininess, const glm::vec3& objectNormal, const glm::vec3& objectPosition ) { glm::vec3 VertexToEye = glm::normalize(eyePosition - objectPosition); glm::vec3 LightReflect = glm::normalize(glm::reflect(lightIncidence, objectNormal)); float SpecularFactor = glm::pow(glm::max(glm::dot(VertexToEye, LightReflect),0.f), materialShininess); glm::vec3 SpecularColor(0, 0, 0); SpecularColor = SpecularFactor * lightSpecularColor * materialSpecularColor; return SpecularColor; }
#include "raycast.hpp" #include <glm/gtx/intersect.hpp> #include <glm/gtx/normal.hpp > #include <memory> bool object_sphere::intersect( glm::vec3* pos_, glm::vec3* color_, glm::vec3* normal_, float* distance_, const std::array<glm::vec3, 2>& ray_) const{ glm::vec3 nray = glm::normalize(ray_[1] - ray_[0]); float distance; bool valid = glm::intersectRaySphere(ray_[0], nray, _center, _r*_r, distance); //レイが衝突した点の座標 *pos_ = ray_[0] + nray * distance; //色の設定 *color_ = _color; //面法線 // 球中心 → 衝突点 *normal_ = glm::normalize(*pos_ - _center); *distance_ = distance; return valid; } bool object_triangle::intersect( glm::vec3* pos_, glm::vec3* color_, glm::vec3* normal_, float* distance_, const std::array<glm::vec3, 2>& ray_)const { glm::vec3 nray = glm::normalize(ray_[1] - ray_[0]); float distance; glm::vec2 bary; bool valid = glm::intersectRayTriangle( ray_[0], nray, _points[0], _points[1], _points[2], bary, distance ); //レイが衝突した点の座標 *pos_ = ray_[0] + nray * distance; //色の設定 *color_ = _color; //面法線 *normal_ = glm::triangleNormal( _points[0], _points[1], _points[2] ); // normalize済 *distance_ = distance; return valid; } void my_raycast::set(const float startZ_, const float endZ_, const int pxwidth_, const int pxheight_) { _startZ = startZ_; _endZ = endZ_; _pxwidth = pxwidth_; _pxheight = pxheight_; glm::vec3 vecx(1, 0, 0); glm::vec3 vecy(0, 1, 0); g[0] = grid( vecx, vecy, glm::vec3(0, 0, _startZ), 1.f, 1.f, pxwidth_, pxheight_ ); g[1] = grid( vecx, vecy, glm::vec3(0, 0, _endZ), 2.f, 2.f, pxwidth_, pxheight_ ); } std::array<glm::vec3, 2> my_raycast::get_ray(const size_t index)const { return std::array<glm::vec3, 2>{ g[0][index].center(), g[1][index].center() }; } void my_raycast::set_object(const object_sphere& obj_) { _objlist.push_back(std::make_shared<object_sphere>(obj_)); } void my_raycast::set_object(const object_triangle& obj_) { _objlist.push_back(std::make_shared<object_triangle>(obj_)); } void my_raycast::set_light(const light_point& light_) { _light = light_; } void my_raycast::raycast() { // 結果の初期化 float inf = std::numeric_limits<float>::infinity(); _hits.clear(); _hits.resize(_pxwidth * _pxheight, hit_pixel_t( glm::vec3(inf, inf, inf), glm::vec3(0, 0, 0) ) ); for (size_t i = 0; i < _pxwidth * _pxheight; i++) { auto ray = get_ray(i); glm::vec3 p; // レイがヒットした座標 glm::vec3 c; // レイがヒットした位置の色 glm::vec3 n; // レイがヒットした位置の法線 float distance = (std::numeric_limits<float>::max)(); bool hit = false; for (auto _obj : _objlist) { float _tmpdist; glm::vec3 tmp_p; glm::vec3 tmp_c; glm::vec3 tmp_n;
if (_obj->intersect(&tmp_p, &tmp_c, &tmp_n, &_tmpdist, ray) == true) { if (_tmpdist < distance) { p = tmp_p; c = tmp_c; n = tmp_n; } hit = true; };
if (hit) { _hits[i]._position = p; // ライトの入射ベクトル glm::vec3 lightIncidence = -_light.calc_vector_point_to_light(p); glm::vec3 diffuse = calc_diffuse(_light.diffuse_color(), c, lightIncidence, n); glm::vec3 specular = calc_specular( get_eye_position(), _light.specular_color(), c, lightIncidence, 80, n, p); glm::vec3 ambient = calc_ambient( _light.ambient_color(), c ); _hits[i]._color = ambient + diffuse + specular; }
} } }
#include <iostream> #include <array> #include<GL/freeglut.h> #include<gl/GL.h> #include "raycast.hpp" #include<glm/gtc/type_ptr.hpp> #include<glm/gtc/matrix_transform.hpp> my_raycast mydata; //! @brief グリッドを表示 //! @param [in] g グリッドオブジェクト //! @param [in] dispcenter セルの中央を表示するか void drawGrid(const grid& g, const bool dispcenter) { glLineWidth(1); glColor3d(1, 1, 1); for (size_t i = 0; i < g.cellCount(); i++) { glBegin(GL_LINE_LOOP); glVertex3fv(glm::value_ptr(g.p0(i))); glVertex3fv(glm::value_ptr(g.p1(i))); glVertex3fv(glm::value_ptr(g.p2(i))); glVertex3fv(glm::value_ptr(g.p3(i))); glEnd(); } if (dispcenter) { glPointSize(1); glColor3d(1, 1, 1); glBegin(GL_POINTS); for (size_t i = 0; i < g.cellCount(); i++) { glVertex3fv( glm::value_ptr(g[i].center())); } glEnd(); } } void drawXYZ() { glLineWidth(3); glBegin(GL_LINES); glColor3f(1, 0, 0); glVertex3f(0, 0, 0); glVertex3f(1, 0, 0); glColor3f(0, 1, 0); glVertex3f(0, 0, 0); glVertex3f(0, 1, 0); glColor3f(0, 0, 1); glVertex3f(0, 0, 0); glVertex3f(0, 0, 1); glEnd(); glLineWidth(1); } void drawRays() { glBegin(GL_LINES); for (size_t i = 0; i < mydata.ray_count(); i++) { auto ray = mydata.get_ray(i); glColor3f(1.0, 1.0, 1.0); glVertex3fv(glm::value_ptr(ray[0])); glColor3f(0.0, 0.0, 1.0); glVertex3fv(glm::value_ptr(ray[1])); } glEnd(); } //ウィンドウの幅と高さ int width, height; //描画関数 void disp(void) { glViewport(0, 0, width, height); glClearColor(0.2, 0.2, 0.2, 1); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glm::mat4 proj = glm::perspectiveFov(glm::radians(45.f), (float)width, (float)height, 0.1f, 50.f); glLoadMatrixf(glm::value_ptr(proj)); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); ////////////////////////////////////////// ////////////////////////////////////////// // カメラの設定 float z = mydata.lookatZ(); glm::mat4 look = glm::lookAt( glm::vec3(5, 4, z-5 ),// eye glm::vec3(0, 0, z ), // lookat glm::vec3(0,1, 0) // up ); glLoadMatrixf(glm::value_ptr(look)); ////////////////////////////////////////// // グリッドを表示 drawGrid(mydata.get_grid(0), false);//from drawGrid(mydata.get_grid(1), false);//to ////////////////////////////////////////// // 作成したレイを表示 //drawRays(); ////////////////////////////////////////// glPointSize(3); glBegin(GL_POINTS); for (size_t i = 0; i < mydata.get_intersects().size(); i++) { glColor3fv( glm::value_ptr( mydata.get_intersects()[i]._color ) ); glVertex3fv( glm::value_ptr(mydata.get_intersects()[i]._position) ); } glEnd(); glPointSize(1); drawXYZ(); glPointSize(3); glBegin(GL_POINTS); for (size_t i = 0; i < mydata.get_intersects().size(); i++) { if (isinf(mydata.get_intersects()[i]._position.x) == false) { glColor3fv( glm::value_ptr( mydata.get_intersects()[i]._color ) ); glVertex3fv( glm::value_ptr( mydata.get_grid(0)[i].center() ) ); } } glEnd(); glPointSize(1); glColor3d(1, 1, 0); glPointSize(10); glBegin(GL_POINTS); glVertex3fv(glm::value_ptr(mydata.get_light().position())); glEnd(); glPointSize(1); glColor3d(1, 0, 0); glColor3d(0, 0, 1); glPointSize(35); glBegin(GL_POINTS); glm::vec3 eyepos = mydata.get_eye_position(); glVertex3fv(glm::value_ptr(eyepos)); glEnd(); glPointSize(1); glFlush(); } //ウィンドウサイズの変化時に呼び出される void reshape(int w, int h) { width = w; height = h; disp(); } //エントリポイント int main(int argc, char** argv) { glutInit(&argc, argv); glutInitWindowPosition(100, 50); glutInitWindowSize(500, 500); glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA); mydata.set(0.f, 3.f,100,100); /////////////////////////////////// mydata.set_object( // 球の登録 object_sphere( glm::vec3(0.0, 0.0, 2), // 球の中心 glm::vec3(1.0, 0, 0), // 球の色 0.5 ) ); mydata.set_object( // 三角形の登録 object_triangle( std::array< glm::vec3, 3>{ glm::vec3(0.916876, -0.840909, 1.58713), glm::vec3(-0.633301, -0.922765, 0.585358), glm::vec3(-0.513684, 0.453145, 1.330333) }, glm::vec3(1, 1, 0) ) ); mydata.set_light( light_point( glm::vec3(1.0, 1.0, 0.5), // ライトの位置 glm::vec3(0.5, 0.5, 0.5), // diffuse色 glm::vec3(0.2, 0.2, 0.2), // ambient色 glm::vec3(1.0, 1.0, 1.0) // specular色 ) ); mydata.raycast(); glutCreateWindow("sample"); glutDisplayFunc(disp); glutReshapeFunc(reshape); glutMainLoop(); return 0; }