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)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
if(POLICY CMP0175)
|
||||
|
|
|
|||
5
Cargo.lock
generated
5
Cargo.lock
generated
|
|
@ -640,6 +640,7 @@ version = "0.1.0"
|
|||
dependencies = [
|
||||
"glam",
|
||||
"hyperboloid",
|
||||
"libc",
|
||||
"pollster",
|
||||
"raw-window-handle",
|
||||
"wgpu",
|
||||
|
|
@ -716,9 +717,9 @@ checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc"
|
|||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.180"
|
||||
version = "0.2.186"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc"
|
||||
checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
|
||||
|
||||
[[package]]
|
||||
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>(
|
||||
make_surface: impl FnOnce(&wgpu::Instance) -> Result<wgpu::Surface<'static>, E>,
|
||||
) -> Result<Gpu, Box<dyn Error>> {
|
||||
|
|
|
|||
|
|
@ -13,3 +13,4 @@ glam = { version = "0.30" }
|
|||
pollster = "0.4.0"
|
||||
raw-window-handle = "0.6.2"
|
||||
wgpu = "27.0.1"
|
||||
libc = "0.2.186"
|
||||
|
|
|
|||
|
|
@ -1,13 +1,17 @@
|
|||
#include "api.hxx"
|
||||
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
|
||||
#include <QByteArray>
|
||||
|
||||
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" 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_redraw(Core* viewport, const RedrawArgs* args);
|
||||
extern "C" ByteArray rt4_make_obj(const ShapeArgs* args);
|
||||
} // namespace ffi
|
||||
|
||||
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 {
|
||||
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
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
struct xcb_connection_t;
|
||||
|
||||
class QByteArray;
|
||||
|
||||
struct alignas(16) Vec4 {
|
||||
float x, y, z, w;
|
||||
};
|
||||
|
|
@ -11,6 +14,11 @@ struct alignas(16) Vec4 {
|
|||
namespace ffi {
|
||||
struct Core;
|
||||
|
||||
struct ByteArray {
|
||||
std::byte* data;
|
||||
std::size_t length;
|
||||
};
|
||||
|
||||
enum class Mode: std::uint32_t {
|
||||
Solid,
|
||||
Shell,
|
||||
|
|
@ -82,3 +90,5 @@ public:
|
|||
private:
|
||||
MutCore ptr;
|
||||
};
|
||||
|
||||
QByteArray make_obj(const ShapeArgs& args);
|
||||
|
|
|
|||
|
|
@ -1,9 +1,15 @@
|
|||
use std::{ffi::c_void, num::NonZero, ptr::NonNull};
|
||||
|
||||
use hyperboloid::{Core, RedrawArgs, init_gpu_inner};
|
||||
use glam::{UVec2, uvec2};
|
||||
use hyperboloid::{Core, RedrawArgs, ShapeArgs, init_gpu_inner, write_obj};
|
||||
use raw_window_handle::{RawDisplayHandle, RawWindowHandle, XcbDisplayHandle, XcbWindowHandle};
|
||||
|
||||
#[repr(C)]
|
||||
pub struct ByteArray {
|
||||
data: *mut u8,
|
||||
length: usize,
|
||||
}
|
||||
|
||||
unsafe fn create_viewport(
|
||||
display: impl Into<RawDisplayHandle>,
|
||||
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) {
|
||||
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 <QFileDialog>
|
||||
|
||||
#include "ui_main_window.h"
|
||||
|
||||
Hyperboloid::Hyperboloid(QWidget* parent)
|
||||
: QMainWindow(parent),
|
||||
m_ui(new Ui::MainWindow) {
|
||||
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->modeShell, (int)Mode::Shell);
|
||||
m_ui->mode->setId(m_ui->modeMesh, (int)Mode::Mesh);
|
||||
|
|
@ -31,12 +34,8 @@ void Hyperboloid::updateModeUi() {
|
|||
m_ui->lineWidth->setEnabled(has_line_width);
|
||||
}
|
||||
|
||||
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 ShapeArgs shape {
|
||||
ffi::ShapeArgs Hyperboloid::makeShapeArgs() const {
|
||||
return {
|
||||
.mode = modeFromInt(m_ui->mode->checkedId()),
|
||||
.sides = (std::uint32_t)m_ui->sides->value(),
|
||||
.layers_up = (std::uint32_t)m_ui->layersUp->value(),
|
||||
|
|
@ -46,8 +45,15 @@ void Hyperboloid::updateView() {
|
|||
.thickness = (float)m_ui->thickness->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{
|
||||
.shape = shape,
|
||||
.shape = makeShapeArgs(),
|
||||
.background = { color.redF(), color.greenF(), color.blueF(), 1.00 },
|
||||
.camera_yaw = qDegreesToRadians((float)m_ui->camYaw->value()),
|
||||
.camera_pitch = qDegreesToRadians((float)m_ui->camPitch->value()),
|
||||
|
|
@ -57,4 +63,10 @@ void Hyperboloid::updateView() {
|
|||
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"
|
||||
|
|
|
|||
|
|
@ -7,6 +7,10 @@ namespace Ui {
|
|||
class MainWindow;
|
||||
}
|
||||
|
||||
namespace ffi {
|
||||
struct ShapeArgs;
|
||||
}
|
||||
|
||||
class Hyperboloid : public QMainWindow {
|
||||
Q_OBJECT
|
||||
|
||||
|
|
@ -17,7 +21,10 @@ public:
|
|||
public slots:
|
||||
void updateModeUi();
|
||||
void updateView();
|
||||
void on_actionExport_triggered();
|
||||
|
||||
private:
|
||||
const std::unique_ptr<Ui::MainWindow> m_ui;
|
||||
|
||||
ffi::ShapeArgs makeShapeArgs() const;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -375,6 +375,22 @@
|
|||
</layout>
|
||||
</widget>
|
||||
</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>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
|
|
@ -391,6 +407,20 @@
|
|||
</layout>
|
||||
</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>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user