1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206
package my3d; import java.util.ArrayList; import javafx.scene.canvas.Canvas; import javafx.scene.paint.Color; import my3d._math3d.*; import my3d._shape3d.*; public class _viewport3d2 extends _viewport3d { private static final int ALLOC_RD_BUF_SIZE = 50; public _viewport3d2(Canvas view) { super(view); render_flag = 0; for (int i = 0; i < alloc_rd_buf.length; i++) alloc_rd_buf[i] = new _render_data(); pick_color = Color.RED; } private boolean b_zsort; public boolean is_zsort_enable() { return b_zsort; } public void set_zsort_enable(boolean b) { b_zsort = b; } private static final int RENDER_ZSORT = 0x1; private int render_flag; private int shape_index; @Override public void begin_render() { super.begin_render(); if (b_zsort) render_flag |= RENDER_ZSORT; if (render_flag != 0) { rd_list.clear(); alloc_rd_clear(); } shape_index = 0; } protected boolean is_picked(_shape3d shape, _face face) { return pick_mode != _pick_mode.None && (shape_index == pick_shape_index) && (pick_mode == _pick_mode.Shape || shape.faces.indexOf(face) == pick_face_index); } @Override protected Color get_color(_shape3d shape, _face face) { if (is_picked(shape, face)) return pick_color; return super.get_color(shape, face); } @Override public void end_render() { while (render_flag != 0) { // Zソート法 if ((render_flag & RENDER_ZSORT) != 0) { rd_list.sort((rd1, rd2) -> (int)Math.signum(rd2.z - rd1.z)); render_flag &= ~RENDER_ZSORT; } // ピック処理 else if ((render_flag & RENDER_PICK) != 0) { pick_listener.picked(pick_shape_index, pick_face_index); render_flag &= ~RENDER_PICK; } // 描画情報を再送 for (_render_data rd : rd_list) { tf_curr = rd.tf; shape_index = rd.index; render_shape(rd.shape); } } } @Override public void render_shape(_shape3d shape) { if (pre_render_shape(shape)) return; super.render_shape(shape); } protected boolean pre_render_shape(_shape3d shape) { if ((render_flag & RENDER_ZSORT) != 0) { // Zソート法 _vector v = shape.center; v = (v != null) ? _math3d.mul(tf_curr, v, v_buf1) : tf_curr.position; add_render_data(shape, tf_curr, v.z); return true; } else if ((render_flag & RENDER_PICK) != 0) { // ビック処理 _vector[] vertices = shape.vertices; for (int i = 0; i < vertices.length; i++) _math3d.mul(tf_curr, vertices[i], va_buf[i]); ArrayList<_face> faces = shape.faces; for (_face face : faces) { if (pick_face(face, va_buf)) { pick_shape_index = shape_index; pick_face_index = faces.indexOf(face); break; } } add_render_data(shape, tf_curr, 0); return true; } return false; } private static class _render_data { public _shape3d shape; public _transform tf; public double z; public int index; public _render_data set(_shape3d shape, _transform tf, double z, int index) { this.shape = shape; this.tf = tf; this.z = z; this.index = index; return this; } } private _render_data[] alloc_rd_buf = new _render_data[ALLOC_RD_BUF_SIZE]; private int alloc_rd_pos = 0; private void alloc_rd_clear() { alloc_rd_pos = 0; } private _render_data alloc_rd() { return alloc_rd_buf[alloc_rd_pos++]; } private ArrayList<_render_data> rd_list = new ArrayList<>(); private void add_render_data(_shape3d shape, _transform tf, double z) { if (shape_index < rd_list.size()) return; _render_data rd = alloc_rd(); rd.set(shape, tf, z, shape_index++); rd_list.add(rd); } // ピック処理 public static enum _pick_mode { Shape, Face, None } public static interface _pick_listener { public void picked(int shape_index, int face_index); } private static final int RENDER_PICK = 0x2; private int pick_shape_index = -1, pick_face_index = -1; private _vector v_pick = new _vector(); private _pick_mode pick_mode = _pick_mode.None; public void set_pick_mode(_pick_mode pick_mode) { this.pick_mode = pick_mode; } private Color pick_color; private _pick_listener pick_listener; public void pick(double x, double y, _pick_listener lis) { pick_shape_index = -1; pick_face_index = -1; _math3d.mul(get_ray(x, y, v_pick), clip_back, v_pick); render_flag |= RENDER_PICK; pick_listener = lis; } private boolean pick_face(_face face, _vector[] vertices) { int n = get_vertices(face.vertex_indices, vertices, va, v_buf_normal); if (n == 0) return false; double len = _math3d.dot(va[0], v_buf_normal); double k = len / _math3d.dot(v_pick, v_buf_normal); if (k > 1 || k < 0) return false; _math3d.mul(v_pick, k, v_buf1); int index = get_max_index(v_buf_normal); _vector v1, v2, v3; double s1, s2, s3; for (int i = 0; i < n - 2; i++) { v1 = va[0]; v2 = va[i + 1]; v3 = va[i + 2]; s1 = get_square(v_buf1, v2, v3, index); s2 = get_square(v_buf1, v3, v1, index); s3 = get_square(v_buf1, v1, v2, index); if ((s1 < 0 == s2 < 0) && (s2 < 0 == s3 < 0)) { v_pick.set(v_buf1); return true; } } return false; } protected double get_square( _vector v1, _vector v2, _vector v3, int index) { double x1, x2, y1, y2; if (index == 0) { x1 = v2.y - v1.y; x2 = v3.y - v1.y; y1 = v2.z - v1.z; y2 = v3.z - v1.z; } else if (index == 1) { x1 = v2.z - v1.z; x2 = v3.z - v1.z; y1 = v2.x - v1.x; y2 = v3.x - v1.x; } else { x1 = v2.x - v1.x; x2 = v3.x - v1.x; y1 = v2.y - v1.y; y2 = v3.y - v1.y; } return x1 * y2 - x2 * y1; } protected int get_max_index(_vector v) { double x = Math.abs(v.x), y = Math.abs(v.y), z = Math.abs(v.z); return (x > y) ? (x > z ? 0 : 2) : (y > z ? 1 : 2); } }