add export
This commit is contained in:
parent
cf72e340c7
commit
d0155e00f7
|
|
@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.18)
|
||||||
|
|
||||||
project(hyperboloid VERSION 1.0.0 LANGUAGES CXX)
|
project(hyperboloid VERSION 1.0.0 LANGUAGES CXX)
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 17)
|
set(CMAKE_CXX_STANDARD 20)
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
|
||||||
if(POLICY CMP0175)
|
if(POLICY CMP0175)
|
||||||
|
|
|
||||||
5
Cargo.lock
generated
5
Cargo.lock
generated
|
|
@ -640,6 +640,7 @@ version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"glam",
|
"glam",
|
||||||
"hyperboloid",
|
"hyperboloid",
|
||||||
|
"libc",
|
||||||
"pollster",
|
"pollster",
|
||||||
"raw-window-handle",
|
"raw-window-handle",
|
||||||
"wgpu",
|
"wgpu",
|
||||||
|
|
@ -716,9 +717,9 @@ checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.180"
|
version = "0.2.186"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc"
|
checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libloading"
|
name = "libloading"
|
||||||
|
|
|
||||||
13
src/lib.rs
13
src/lib.rs
|
|
@ -229,6 +229,19 @@ impl Core {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn write_obj(mut target: impl std::io::Write, args: &ShapeArgs) -> std::io::Result<()> {
|
||||||
|
let mesh = mesh::make(args);
|
||||||
|
writeln!(target, "o Hyperboloid")?;
|
||||||
|
for v in mesh.vertices {
|
||||||
|
writeln!(target, "v {} {} {}", v.x, v.y, v.z)?;
|
||||||
|
}
|
||||||
|
for f in mesh.faces {
|
||||||
|
writeln!(target, "f {} {} {}", f[0] + 1, f[1] + 1, f[2] + 1)?;
|
||||||
|
}
|
||||||
|
target.flush()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn init_gpu_inner<E: Error + 'static>(
|
pub async fn init_gpu_inner<E: Error + 'static>(
|
||||||
make_surface: impl FnOnce(&wgpu::Instance) -> Result<wgpu::Surface<'static>, E>,
|
make_surface: impl FnOnce(&wgpu::Instance) -> Result<wgpu::Surface<'static>, E>,
|
||||||
) -> Result<Gpu, Box<dyn Error>> {
|
) -> Result<Gpu, Box<dyn Error>> {
|
||||||
|
|
|
||||||
|
|
@ -13,3 +13,4 @@ glam = { version = "0.30" }
|
||||||
pollster = "0.4.0"
|
pollster = "0.4.0"
|
||||||
raw-window-handle = "0.6.2"
|
raw-window-handle = "0.6.2"
|
||||||
wgpu = "27.0.1"
|
wgpu = "27.0.1"
|
||||||
|
libc = "0.2.186"
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,17 @@
|
||||||
#include "api.hxx"
|
#include "api.hxx"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
|
#include <QByteArray>
|
||||||
|
|
||||||
namespace ffi {
|
namespace ffi {
|
||||||
extern "C" Core* rt4_viewport_create(xcb_connection_t* connection, std::uint32_t window, std::uint32_t width, std::uint32_t height);
|
extern "C" Core* rt4_viewport_create(xcb_connection_t* connection, std::uint32_t window, std::uint32_t width, std::uint32_t height);
|
||||||
extern "C" void rt4_viewport_destroy(Core* viewport);
|
extern "C" void rt4_viewport_destroy(Core* viewport);
|
||||||
extern "C" void rt4_viewport_configure(Core* viewport, std::uint32_t width, std::uint32_t height);
|
extern "C" void rt4_viewport_configure(Core* viewport, std::uint32_t width, std::uint32_t height);
|
||||||
extern "C" void rt4_viewport_redraw(Core* viewport, const RedrawArgs* args);
|
extern "C" void rt4_viewport_redraw(Core* viewport, const RedrawArgs* args);
|
||||||
|
extern "C" ByteArray rt4_make_obj(const ShapeArgs* args);
|
||||||
} // namespace ffi
|
} // namespace ffi
|
||||||
|
|
||||||
BoxCore::BoxCore(BoxCore&& b)
|
BoxCore::BoxCore(BoxCore&& b)
|
||||||
|
|
@ -55,3 +59,11 @@ void MutCore::configure(std::uint32_t width, std::uint32_t height) const {
|
||||||
void MutCore::redraw(const RedrawArgs& args) const {
|
void MutCore::redraw(const RedrawArgs& args) const {
|
||||||
rt4_viewport_redraw(use(), &args);
|
rt4_viewport_redraw(use(), &args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
using free_deleter = decltype([](void* ptr) { free(ptr); });
|
||||||
|
|
||||||
|
QByteArray make_obj(const ShapeArgs& args) {
|
||||||
|
ffi::ByteArray arr = rt4_make_obj(&args);
|
||||||
|
std::unique_ptr<void, free_deleter> ptr(arr.data);
|
||||||
|
return QByteArray(reinterpret_cast<const char*>(arr.data), arr.length);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,12 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
struct xcb_connection_t;
|
struct xcb_connection_t;
|
||||||
|
|
||||||
|
class QByteArray;
|
||||||
|
|
||||||
struct alignas(16) Vec4 {
|
struct alignas(16) Vec4 {
|
||||||
float x, y, z, w;
|
float x, y, z, w;
|
||||||
};
|
};
|
||||||
|
|
@ -11,6 +14,11 @@ struct alignas(16) Vec4 {
|
||||||
namespace ffi {
|
namespace ffi {
|
||||||
struct Core;
|
struct Core;
|
||||||
|
|
||||||
|
struct ByteArray {
|
||||||
|
std::byte* data;
|
||||||
|
std::size_t length;
|
||||||
|
};
|
||||||
|
|
||||||
enum class Mode: std::uint32_t {
|
enum class Mode: std::uint32_t {
|
||||||
Solid,
|
Solid,
|
||||||
Shell,
|
Shell,
|
||||||
|
|
@ -82,3 +90,5 @@ public:
|
||||||
private:
|
private:
|
||||||
MutCore ptr;
|
MutCore ptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
QByteArray make_obj(const ShapeArgs& args);
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,15 @@
|
||||||
use std::{ffi::c_void, num::NonZero, ptr::NonNull};
|
use std::{ffi::c_void, num::NonZero, ptr::NonNull};
|
||||||
|
|
||||||
use hyperboloid::{Core, RedrawArgs, init_gpu_inner};
|
|
||||||
use glam::{UVec2, uvec2};
|
use glam::{UVec2, uvec2};
|
||||||
|
use hyperboloid::{Core, RedrawArgs, ShapeArgs, init_gpu_inner, write_obj};
|
||||||
use raw_window_handle::{RawDisplayHandle, RawWindowHandle, XcbDisplayHandle, XcbWindowHandle};
|
use raw_window_handle::{RawDisplayHandle, RawWindowHandle, XcbDisplayHandle, XcbWindowHandle};
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct ByteArray {
|
||||||
|
data: *mut u8,
|
||||||
|
length: usize,
|
||||||
|
}
|
||||||
|
|
||||||
unsafe fn create_viewport(
|
unsafe fn create_viewport(
|
||||||
display: impl Into<RawDisplayHandle>,
|
display: impl Into<RawDisplayHandle>,
|
||||||
window: impl Into<RawWindowHandle>,
|
window: impl Into<RawWindowHandle>,
|
||||||
|
|
@ -46,3 +52,16 @@ unsafe extern "C" fn rt4_viewport_configure(viewport: &mut Core, width: u32, hei
|
||||||
unsafe extern "C" fn rt4_viewport_redraw(viewport: &mut Core, args: &RedrawArgs) {
|
unsafe extern "C" fn rt4_viewport_redraw(viewport: &mut Core, args: &RedrawArgs) {
|
||||||
viewport.redraw(args);
|
viewport.redraw(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
unsafe extern "C" fn rt4_make_obj(args: &ShapeArgs) -> ByteArray {
|
||||||
|
let mut buf = Vec::new();
|
||||||
|
write_obj(&mut buf, args).expect("writing to a Vec is infallible");
|
||||||
|
let data = unsafe { libc::malloc(buf.len()) };
|
||||||
|
assert!(!data.is_null());
|
||||||
|
unsafe { libc::memcpy(data, buf.as_ptr() as *const c_void, buf.len()) };
|
||||||
|
ByteArray {
|
||||||
|
data: data as *mut u8,
|
||||||
|
length: buf.len(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,14 @@
|
||||||
#include "main_window.hxx"
|
#include "main_window.hxx"
|
||||||
|
|
||||||
|
#include <QFileDialog>
|
||||||
|
|
||||||
#include "ui_main_window.h"
|
#include "ui_main_window.h"
|
||||||
|
|
||||||
Hyperboloid::Hyperboloid(QWidget* parent)
|
Hyperboloid::Hyperboloid(QWidget* parent)
|
||||||
: QMainWindow(parent),
|
: QMainWindow(parent),
|
||||||
m_ui(new Ui::MainWindow) {
|
m_ui(new Ui::MainWindow) {
|
||||||
m_ui->setupUi(this);
|
m_ui->setupUi(this);
|
||||||
|
m_ui->btnExport->setDefaultAction(m_ui->actionExport);
|
||||||
m_ui->mode->setId(m_ui->modeSolid, (int)Mode::Solid);
|
m_ui->mode->setId(m_ui->modeSolid, (int)Mode::Solid);
|
||||||
m_ui->mode->setId(m_ui->modeShell, (int)Mode::Shell);
|
m_ui->mode->setId(m_ui->modeShell, (int)Mode::Shell);
|
||||||
m_ui->mode->setId(m_ui->modeMesh, (int)Mode::Mesh);
|
m_ui->mode->setId(m_ui->modeMesh, (int)Mode::Mesh);
|
||||||
|
|
@ -31,12 +34,8 @@ void Hyperboloid::updateModeUi() {
|
||||||
m_ui->lineWidth->setEnabled(has_line_width);
|
m_ui->lineWidth->setEnabled(has_line_width);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Hyperboloid::updateView() {
|
ffi::ShapeArgs Hyperboloid::makeShapeArgs() const {
|
||||||
const int max_layers = (m_ui->sides->value() - 1) / 2;
|
return {
|
||||||
m_ui->layersUp->setMaximum(max_layers);
|
|
||||||
m_ui->layersDown->setMaximum(max_layers);
|
|
||||||
const auto color = m_ui->inBackground->color();
|
|
||||||
const ShapeArgs shape {
|
|
||||||
.mode = modeFromInt(m_ui->mode->checkedId()),
|
.mode = modeFromInt(m_ui->mode->checkedId()),
|
||||||
.sides = (std::uint32_t)m_ui->sides->value(),
|
.sides = (std::uint32_t)m_ui->sides->value(),
|
||||||
.layers_up = (std::uint32_t)m_ui->layersUp->value(),
|
.layers_up = (std::uint32_t)m_ui->layersUp->value(),
|
||||||
|
|
@ -46,8 +45,15 @@ void Hyperboloid::updateView() {
|
||||||
.thickness = (float)m_ui->thickness->value(),
|
.thickness = (float)m_ui->thickness->value(),
|
||||||
.line_width = (float)m_ui->lineWidth->value(),
|
.line_width = (float)m_ui->lineWidth->value(),
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
void Hyperboloid::updateView() {
|
||||||
|
const int max_layers = (m_ui->sides->value() - 1) / 2;
|
||||||
|
m_ui->layersUp->setMaximum(max_layers);
|
||||||
|
m_ui->layersDown->setMaximum(max_layers);
|
||||||
|
const auto color = m_ui->inBackground->color();
|
||||||
const RedrawArgs args{
|
const RedrawArgs args{
|
||||||
.shape = shape,
|
.shape = makeShapeArgs(),
|
||||||
.background = { color.redF(), color.greenF(), color.blueF(), 1.00 },
|
.background = { color.redF(), color.greenF(), color.blueF(), 1.00 },
|
||||||
.camera_yaw = qDegreesToRadians((float)m_ui->camYaw->value()),
|
.camera_yaw = qDegreesToRadians((float)m_ui->camYaw->value()),
|
||||||
.camera_pitch = qDegreesToRadians((float)m_ui->camPitch->value()),
|
.camera_pitch = qDegreesToRadians((float)m_ui->camPitch->value()),
|
||||||
|
|
@ -57,4 +63,10 @@ void Hyperboloid::updateView() {
|
||||||
m_ui->viewport->setView(args);
|
m_ui->viewport->setView(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Hyperboloid::on_actionExport_triggered() {
|
||||||
|
const auto shape = makeShapeArgs();
|
||||||
|
const auto obj = make_obj(shape);
|
||||||
|
QFileDialog::saveFileContent(obj, "hyperboloid.obj", this);
|
||||||
|
}
|
||||||
|
|
||||||
#include "moc_main_window.cpp"
|
#include "moc_main_window.cpp"
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,10 @@ namespace Ui {
|
||||||
class MainWindow;
|
class MainWindow;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace ffi {
|
||||||
|
struct ShapeArgs;
|
||||||
|
}
|
||||||
|
|
||||||
class Hyperboloid : public QMainWindow {
|
class Hyperboloid : public QMainWindow {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
|
|
@ -17,7 +21,10 @@ public:
|
||||||
public slots:
|
public slots:
|
||||||
void updateModeUi();
|
void updateModeUi();
|
||||||
void updateView();
|
void updateView();
|
||||||
|
void on_actionExport_triggered();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const std::unique_ptr<Ui::MainWindow> m_ui;
|
const std::unique_ptr<Ui::MainWindow> m_ui;
|
||||||
|
|
||||||
|
ffi::ShapeArgs makeShapeArgs() const;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -375,6 +375,22 @@
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QToolButton" name="btnExport">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>&Export...</string>
|
||||||
|
</property>
|
||||||
|
<property name="toolButtonStyle">
|
||||||
|
<enum>Qt::ToolButtonStyle::ToolButtonTextBesideIcon</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<spacer name="verticalSpacer">
|
<spacer name="verticalSpacer">
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
|
|
@ -391,6 +407,20 @@
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</widget>
|
</widget>
|
||||||
|
<action name="actionExport">
|
||||||
|
<property name="icon">
|
||||||
|
<iconset theme="QIcon::ThemeIcon::DocumentSaveAs"/>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>&Export...</string>
|
||||||
|
</property>
|
||||||
|
<property name="shortcut">
|
||||||
|
<string>Ctrl+E</string>
|
||||||
|
</property>
|
||||||
|
<property name="menuRole">
|
||||||
|
<enum>QAction::MenuRole::NoRole</enum>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
</widget>
|
</widget>
|
||||||
<customwidgets>
|
<customwidgets>
|
||||||
<customwidget>
|
<customwidget>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user