From 4842ab0e4f6fc2cb1a35c2ec646acade788fbe4d Mon Sep 17 00:00:00 2001 From: Zack Buhman Date: Sat, 27 Jan 2024 10:03:01 +0800 Subject: [PATCH] example: add clipping2 Unlike the previous clipping example, this clipping example preserves surface normals. --- example/clipping2.cpp | 294 ++++++++++++++++++++++++++++++++++++++++++ example/example.mk | 14 ++ geometry/triangle.hpp | 28 ++++ geometry/triangle.obj | 12 ++ math/geometry.hpp | 119 +++++++++++++++++ sh7091/serial.cpp | 14 +- sh7091/serial.hpp | 3 + 7 files changed, 482 insertions(+), 2 deletions(-) create mode 100644 example/clipping2.cpp create mode 100644 geometry/triangle.hpp create mode 100644 geometry/triangle.obj create mode 100644 math/geometry.hpp diff --git a/example/clipping2.cpp b/example/clipping2.cpp new file mode 100644 index 0000000..c632979 --- /dev/null +++ b/example/clipping2.cpp @@ -0,0 +1,294 @@ +#include +#include + +#include "align.hpp" + +#include "vga.hpp" +#include "holly/holly.hpp" +#include "holly/core.hpp" +#include "holly/core_bits.hpp" +#include "holly/ta_fifo_polygon_converter.hpp" +#include "holly/ta_parameter.hpp" +#include "holly/ta_vertex_parameter.hpp" +#include "holly/ta_global_parameter.hpp" +#include "holly/ta_bits.hpp" +#include "holly/isp_tsp.hpp" +#include "holly/region_array.hpp" +#include "holly/background.hpp" +#include "holly/texture_memory_alloc.hpp" +#include "memorymap.hpp" +#include "sh7091/serial.hpp" + +#include "geometry/triangle.hpp" +#include "geometry/circle.hpp" +#include "math/vec4.hpp" +#include "math/math.hpp" +#include "math/geometry.hpp" + +#include "maple/maple.hpp" +#include "maple/maple_impl.hpp" +#include "maple/maple_bus_bits.hpp" +#include "maple/maple_bus_commands.hpp" +#include "maple/maple_bus_ft0.hpp" + +uint32_t _command_buf[1024 / 4 + 32]; +uint32_t _receive_buf[1024 / 4 + 32]; + +static ft0::data_transfer::data_format data[4]; + +void do_get_condition(uint32_t * command_buf, + uint32_t * receive_buf) +{ + using command_type = get_condition; + using response_type = data_transfer; + + get_condition::data_fields data_fields = { + .function_type = std::byteswap(function_type::controller) + }; + + maple::init_host_command_all_ports(command_buf, receive_buf, + data_fields); + maple::dma_start(command_buf); + + using command_response_type = struct maple::command_response; + for (uint8_t port = 0; port < 4; port++) { + auto response = reinterpret_cast(receive_buf); + auto& bus_data = response[port].bus_data; + if (bus_data.command_code != response_type::command_code) { + return; + } + auto& data_fields = bus_data.data_fields; + if ((data_fields.function_type & std::byteswap(function_type::controller)) == 0) { + return; + } + + data[port].analog_axis_1 = data_fields.data.analog_axis_1; + data[port].analog_axis_2 = data_fields.data.analog_axis_2; + data[port].analog_axis_3 = data_fields.data.analog_axis_3; + data[port].analog_axis_4 = data_fields.data.analog_axis_4; + } +} + +constexpr vec3 colors[] = { + {1.f, 0.5f, 0.f}, + {0.f, 1.0f, 0.f}, + {0.f, 0.5f, 1.f}, + {1.f, 0.0f, 1.f}, +}; + +void transform1(ta_parameter_writer& parameter, + const vec3& v, + const vec3& c, + bool end_of_strip) +{ + float x = v.x; + float y = v.y; + float z = v.z; + + // camera transform + z += 1; + + // screen space transform + x *= 240.f; + y *= 240.f; + x += 320.f; + y += 240.f; + z = 1 / z; + + parameter.append() = + ta_vertex_parameter::polygon_type_1(polygon_vertex_parameter_control_word(end_of_strip), + x, y, z, + 1.0f, // alpha + c.r, + c.g, + c.b + ); + +} + +void transform(ta_parameter_writer& parameter, + const vec3 * vertices, + const face_vn& face, + const vec4& color, + const vec3& position, + const float theta, + const bool enable_clipping + ) +{ + const uint32_t parameter_control_word = para_control::para_type::polygon_or_modifier_volume + | para_control::list_type::opaque + | obj_control::col_type::floating_color + | obj_control::gouraud; + + const uint32_t isp_tsp_instruction_word = isp_tsp_instruction_word::depth_compare_mode::greater + | isp_tsp_instruction_word::culling_mode::no_culling; + + const uint32_t tsp_instruction_word = tsp_instruction_word::src_alpha_instr::one + | tsp_instruction_word::dst_alpha_instr::zero + | tsp_instruction_word::fog_control::no_fog; + + constexpr uint32_t strip_length = 3; + vec3 points[strip_length]; + + // object transform and clip + for (uint32_t i = 0; i < strip_length; i++) { + uint32_t vertex_ix = face[i].vertex; + auto vertex = vertices[vertex_ix]; + + vertex = (vertex * 0.5f); + + // rotate 90° around the X axis + //float x = vertex.x; + //float y = vertex.z; + //float z = vertex.y; + + float x = vertex.x * cos(theta) - vertex.z * sin(theta); + float y = vertex.x * sin(theta) + vertex.z * cos(theta); + float z = vertex.y; + + // object transform + x += position.x; // object space + y += position.y; // object space + z += position.z; // object space + + // clip + points[i] = vec3(x, y, z); + } + + const vec3 plane_point = {0.f, 0.f, 0.f}; + const vec3 plane_normal = {-1.f, 0.f, 0.f}; + vec3 output[4]; + int output_length = geometry::clip_polygon<3>(output, plane_point, plane_normal, &points[0]); + + if (output_length >= 3) { + parameter.append() = + ta_global_parameter::polygon_type_0(parameter_control_word, + isp_tsp_instruction_word, + tsp_instruction_word, + 0, // texture_control_word + 0, // data_size_for_sort_dma + 0 // next_address_for_sort_dma + ); + transform1(parameter, output[0], colors[0], false); + transform1(parameter, output[1], colors[1], false); + transform1(parameter, output[2], colors[2], true); + } + if (output_length >= 4) { + parameter.append() = + ta_global_parameter::polygon_type_0(parameter_control_word, + isp_tsp_instruction_word, + tsp_instruction_word, + 0, // texture_control_word + 0, // data_size_for_sort_dma + 0 // next_address_for_sort_dma + ); + transform1(parameter, output[0], colors[0], false); + transform1(parameter, output[2], colors[2], false); + transform1(parameter, output[3], colors[3], true); + } + + /* + + A B + D C + + */ +} + +void init_texture_memory(const struct opb_size& opb_size) +{ + auto mem = reinterpret_cast(texture_memory32); + + background_parameter(mem->background, 0xff220000); + + region_array2(mem->region_array, + (offsetof (struct texture_memory_alloc, object_list)), + 640 / 32, // width + 480 / 32, // height + opb_size + ); +} + +uint32_t _ta_parameter_buf[((32 * 8192) + 32) / 4]; + +void main() +{ + uint32_t * command_buf = align_32byte(_command_buf); + uint32_t * receive_buf = align_32byte(_receive_buf); + + vga(); + + // The address of `ta_parameter_buf` must be a multiple of 32 bytes. + // This is mandatory for ch2-dma to the ta fifo polygon converter. + uint32_t * ta_parameter_buf = align_32byte(_ta_parameter_buf); + + constexpr uint32_t ta_alloc = ta_alloc_ctrl::pt_opb::no_list + | ta_alloc_ctrl::tm_opb::no_list + | ta_alloc_ctrl::t_opb::no_list + | ta_alloc_ctrl::om_opb::no_list + | ta_alloc_ctrl::o_opb::_16x4byte; + + constexpr struct opb_size opb_size = { .opaque = 16 * 4 + , .opaque_modifier = 0 + , .translucent = 0 + , .translucent_modifier = 0 + , .punch_through = 0 + }; + + holly.SOFTRESET = softreset::pipeline_soft_reset + | softreset::ta_soft_reset; + holly.SOFTRESET = 0; + + core_init(); + init_texture_memory(opb_size); + + uint32_t frame_ix = 0; + constexpr uint32_t num_frames = 1; + float theta = 0; + float x_pos = 0; + float y_pos = 0; + + while (1) { + do_get_condition(command_buf, receive_buf); + + ta_polygon_converter_init(opb_size.total(), + ta_alloc, + 640 / 32, + 480 / 32); + + const float l_ = static_cast(data[0].analog_axis_1) * (1.f / 255.f); + const float r_ = static_cast(data[0].analog_axis_2) * (1.f / 255.f); + const float t_ = ((l_ > r_) ? l_ : -r_) * 3.14f / 2.f; + + if (t_ > theta) theta += (0.04f * ((t_ - theta) * (t_ - theta))); + else theta -= (0.04f * ((t_ - theta) * (t_ - theta))); + + const float x_ = static_cast(data[0].analog_axis_3 - 0x80) / 127.f; + const float y_ = static_cast(data[0].analog_axis_4 - 0x80) / 127.f; + if (x_ > x_pos) x_pos += (0.02f * ((x_ - x_pos) * (x_ - x_pos))); + else x_pos -= (0.02f * ((x_ - x_pos) * (x_ - x_pos))); + if (y_ > y_pos) y_pos += (0.02f * ((y_ - y_pos) * (y_ - y_pos))); + else y_pos -= (0.02f * ((y_ - y_pos) * (y_ - y_pos))); + + auto parameter = ta_parameter_writer(ta_parameter_buf); + for (uint32_t i = 0; i < circle::num_faces; i++) { + transform(parameter, + circle::vertices, + circle::faces[i], + {1.0f, 1.0f, 0.0f, 0.0f}, // color + {x_pos * 2, y_pos * 2, 0.0f}, // position + theta, + true // clipping + ); + } + + parameter.append() = ta_global_parameter::end_of_list(para_control::para_type::end_of_list); + ta_polygon_converter_transfer(ta_parameter_buf, parameter.offset); + ta_wait_opaque_list(); + core_start_render(frame_ix, num_frames); + + v_sync_in(); + core_wait_end_of_render_video(frame_ix, num_frames); + frame_ix += 1; + } +} diff --git a/example/example.mk b/example/example.mk index 3b89c59..35b3f4a 100644 --- a/example/example.mk +++ b/example/example.mk @@ -211,6 +211,20 @@ CLIPPING_OBJ = \ example/clipping.elf: LDSCRIPT = $(LIB)/alt.lds example/clipping.elf: $(START_OBJ) $(CLIPPING_OBJ) +CLIPPING2_OBJ = \ + example/clipping2.o \ + vga.o \ + holly/core.o \ + holly/region_array.o \ + holly/background.o \ + holly/ta_fifo_polygon_converter.o \ + maple/maple.o \ + sh7091/serial.o \ + $(LIBGCC) + +example/clipping2.elf: LDSCRIPT = $(LIB)/alt.lds +example/clipping2.elf: $(START_OBJ) $(CLIPPING2_OBJ) + MAPLE_DEVICE_REQUEST_OBJ = \ example/maple_device_request.o \ vga.o \ diff --git a/geometry/triangle.hpp b/geometry/triangle.hpp new file mode 100644 index 0000000..a4c7c17 --- /dev/null +++ b/geometry/triangle.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include "geometry.hpp" + +namespace triangle { + constexpr vec3 vertices[] = { + { -1.000000f, 0.000000f, 0.000000f }, + { 1.000000f, 0.000000f, 1.000000f }, + { 1.000000f, 0.000000f, -1.000000f }, + }; + + constexpr vec2 texture[] = { + { 0.000000f, 0.000000f }, + { 1.000000f, 0.000000f }, + { 1.000000f, 1.000000f }, + }; + + constexpr vec3 normals[] = { + { -0.000000f, 1.000000f, -0.000000f }, + }; + + constexpr face_vtn faces[] = { + {{0, 0, 0}, {1, 1, 0}, {2, 2, 0}}, + }; + + constexpr uint32_t num_faces = (sizeof (faces)) / (sizeof (face_vtn)); + +} diff --git a/geometry/triangle.obj b/geometry/triangle.obj new file mode 100644 index 0000000..37ab932 --- /dev/null +++ b/geometry/triangle.obj @@ -0,0 +1,12 @@ +# Blender 3.3.6 +# www.blender.org +o Triangle +v -1.000000 0.000000 0.000000 +v 1.000000 0.000000 1.000000 +v 1.000000 0.000000 -1.000000 +vn -0.0000 1.0000 -0.0000 +vt 0.000000 0.000000 +vt 1.000000 0.000000 +vt 1.000000 1.000000 +s 0 +f 1/1/1 2/2/1 3/3/1 diff --git a/math/geometry.hpp b/math/geometry.hpp new file mode 100644 index 0000000..bd3c39d --- /dev/null +++ b/math/geometry.hpp @@ -0,0 +1,119 @@ +#include + +#include "vec.hpp" + +namespace geometry { + +template +vec line_plane_intersection(const vec& plane_point, // p0 + const vec& plane_normal, // n + const vec& line_start, // l0 + const vec& line_end + ) +{ + const auto line_vector = line_end - line_start; // l + + const T intersection = // d + dot(plane_point - line_start, plane_normal) + / dot(line_vector, plane_normal); + + return line_start + line_vector * intersection; +} + +template +T clip_boundary(const vec& plane_point, // X + const vec& plane_normal, // Nc + const vec& line_point + ) +{ + return dot(plane_normal, line_point - plane_point); +} + +template +inline T positive_modulo(T a, T b) +{ + return (a % b + b) % b; +} + +template +inline int clip_polygon1(vec * output, + const vec plane_point, + const vec plane_normal, + const vec * polygon, + const int ix_s, + const int ix_f, + const bool last_inside) +{ + const vec& s = polygon[ix_s]; + const vec& f = polygon[ix_f]; + + bool this_inside = 0.f < clip_boundary(plane_point, + plane_normal, + f); + + int length = 0; + switch ((last_inside << 1) | (this_inside << 0)) { + case 0b00: // no output + length = 0; + break; + case 0b01: // I, F + length = 2; + { + auto i = line_plane_intersection(plane_point, plane_normal, s, f); + *output++ = i; + *output++ = f; + } + break; + case 0b10: // I + length = 1; + { + auto i = line_plane_intersection(plane_point, plane_normal, s, f); + *output++ = i; + } + break; + case 0b11: // F + length = 1; + *output++ = f; + break; + } + + bool end_of_polygon = ix_f == (polygon_len - 1); + if (!end_of_polygon) { + return length + + clip_polygon1(output, + plane_point, + plane_normal, + polygon, + ix_f, + ix_f + 1, + this_inside); + } else { + return length; + } +} + +template +int clip_polygon(vec * output, + const vec& plane_point, + const vec& plane_normal, + const vec * polygon + ) +{ + const vec f = polygon[polygon_len - 1]; + + // It would be nice to remove the extra dot product, but the + // alternative seems likely uglier. + bool this_inside = 0.f < clip_boundary(plane_point, + plane_normal, + f); + + return clip_polygon1(output, + plane_point, + plane_normal, + polygon, + polygon_len - 1, // ix_s + 0, // ix_f + this_inside); +} + +} diff --git a/sh7091/serial.cpp b/sh7091/serial.cpp index 8011fbf..b3475ed 100644 --- a/sh7091/serial.cpp +++ b/sh7091/serial.cpp @@ -48,7 +48,7 @@ void string(const char * s) } template -void integer(const T n) +void integer(const T n, const char end) { constexpr uint32_t length = (sizeof (T)) * 2; char num_buf[length + 1]; @@ -56,11 +56,21 @@ void integer(const T n) num_buf[length] = 0; string("0x"); string(num_buf); - string("\n"); + character(end); +} + +template +void integer(const T n) +{ + return integer(n, '\n'); } template void integer(uint32_t param); template void integer(uint16_t param); template void integer(uint8_t param); +template void integer(uint32_t param, char end); +template void integer(uint16_t param, char end); +template void integer(uint8_t param, char end); + } diff --git a/sh7091/serial.hpp b/sh7091/serial.hpp index add72ea..64a0f13 100644 --- a/sh7091/serial.hpp +++ b/sh7091/serial.hpp @@ -6,6 +6,9 @@ void character(const char c); void string(const char * s); +template +void integer(const T n, const char end); + template void integer(const T n);