-
-
Save kunugibaru/a205c34dfcf16f9ae6fa4fee0d16a594 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#pragma once | |
/** ====================== | |
Done: テクスチャー付き立方体, カメラの移動、キーボード操作の受付、 | |
Todo: | |
形状と、シェーダーの情報を分離する。 | |
ジオメトリ読み込み, キューブマップ, 法線マップ, ソート | |
アルファ, TriBuffer, mikkt, | |
カメラを UserControllable として一般化? | |
========================= | |
*/ | |
#include <QOpenGLWidget> | |
#include <QOpenGLBuffer> | |
#include <QOpenGLShaderProgram> | |
#include <QtWidgets/QApplication> | |
#include <QMatrix4x4> | |
#include <QVector> | |
#include <QOpenGLShader> | |
#include <QOpenGLVertexArrayObject> | |
#include <QMouseEvent> | |
#include <QOpenGLTexture> | |
#include <QDebug> | |
#include <QWidget> | |
#include <QTimer> | |
#include <QOpenGLFunctions> | |
// use stl containers instead of qt's. | |
#include <fstream> | |
#include <string> | |
#include <iostream> | |
#include <array> | |
#include <vector> | |
#include <functional> | |
#include <map> | |
#include <memory> | |
/** ====================== | |
DEBUG | |
========================= | |
*/ | |
namespace dbg | |
{ | |
template<typename T> | |
inline QString dump_array(const T* ts, int length) | |
{ | |
QString out; | |
for (size_t i = 0; i < length; i++) { | |
out += QString::fromStdString(std::to_string(static_cast<float>(ts[i]))) + ", "; | |
} | |
return out; | |
} | |
inline void ifdef_debug(const std::function<void()>& func) | |
{ | |
#ifdef _DEBUG | |
func(); | |
#endif // _DEBUG | |
} | |
} | |
/** ====================== | |
VECTOR, DATA TYPES | |
========================= | |
*/ | |
#if 0 | |
namespace vecs | |
{ | |
template<typename N, int num> | |
class Vec | |
{ | |
public: | |
std::array<N, num> vec_; | |
N x() const { vec_.at(0); } | |
N y() const { vec_.at(1); } | |
N z() const { vec_.at(2); } | |
}; | |
template<typename N> | |
using Vec3 = Vec<N, 3>; | |
template<typename N> | |
class Vec4 : public Vec<N, 4> | |
{ | |
N a() const { vec_.at(3); } | |
}; | |
} | |
//using Vec3 = vecs::Vec3<float>; | |
//using Vec4 = vecs::Vec4<float>; | |
#endif // 0 | |
using Vec3 = QVector3D; | |
using Vec4 = QVector4D; | |
using Vec2 = QVector2D; | |
using Quaternion = QQuaternion; | |
enum struct UserCommand | |
{ | |
kNone, | |
// Camera Movement Rotations | |
kCamTiltUp, kCamTiltDown, kCamPanLeft, kCamPanRight, | |
// Camera Movement Translations | |
kCamDollyIn, kCamDollyOut, | |
kCamTruckRight, kCamTruckLeft, kCamCraneUp, kCamCraneDown, | |
kCamTruckRightAroundOrigin, kCamTruckLeftAroundOrigin, | |
kCamCraneUpAroundOrigin, kCamCraneDownAroundOrigin, | |
// Camera Misc | |
kCamFocusOrigin, | |
}; | |
/** ====================== | |
CAMERA | |
========================= | |
*/ | |
class Camera | |
{ | |
public: | |
using Matrix = QMatrix4x4; | |
Matrix model_; | |
Matrix perspective_; | |
Matrix view_; | |
float vertical_angle_ = 60.f; | |
Vec3 pos_ = { 2, 3, 5 }; | |
Vec3 target_ = { 0,0,0 }; | |
Vec3 up_ = { 0,1,0 }; | |
float dolly_speed_ = 1.0f; | |
float pan_speed_ = 1.0f; | |
QSize aspect_ratio_; | |
Vec3 forward(bool normalize = true) const | |
{ | |
Vec3 dir = target_ - pos_;; | |
if (normalize) { | |
dir.normalize(); | |
} | |
return dir; | |
} | |
Vec3 back(bool normalize = true) const | |
{ | |
return -1 * forward(normalize); | |
} | |
Vec3 left(bool normalize = true) const | |
{ | |
Vec3 left = QVector3D::crossProduct(this->up_, this->forward()); | |
if (normalize) { | |
left.normalize(); | |
} | |
return left; | |
} | |
Vec3 right(bool normalize = true) const | |
{ | |
return -1 * this->left(normalize); | |
} | |
void translate(const Vec3& angle) | |
{ | |
//qInfo() << dir; | |
this->pos_ += angle * pan_speed_; | |
this->target_ += angle * pan_speed_; | |
} | |
void rotate(const float& angle) | |
{ | |
const Vec3& old_dir = this->pos_ - this->target_; | |
const Vec3& new_dir = Quaternion::fromAxisAndAngle(this->up_, angle).rotatedVector(old_dir); | |
this->target_ = target_ + (old_dir - new_dir); | |
} | |
void rotate_around_targer(const float& angle) | |
{ | |
const Vec3& old_dir = this->target_ - this->pos_; | |
const Vec3& new_dir = Quaternion::fromAxisAndAngle(this->up_, angle).rotatedVector(old_dir); | |
this->pos_ = pos_ + (old_dir - new_dir); | |
} | |
void update() | |
{ | |
perspective_.setToIdentity(); | |
view_.setToIdentity(); | |
model_.setToIdentity(); | |
perspective_.perspective(this->vertical_angle_, aspect_ratio_.width() / aspect_ratio_.height(), 0.001f, 120.0f); | |
this->view_.lookAt(this->pos_, target_, up_); | |
} | |
Matrix mvp() | |
{ | |
return (perspective_ * view_) * model_; | |
} | |
}; | |
/** ====================== | |
MESH | |
========================= | |
*/ | |
/* | |
メッシュ作業中。 | |
面に特殊な情報は一切持たせない方向で。 | |
*/ | |
namespace geo | |
{ | |
struct Placement // 分けないと常に原点からしか回転できなくなる。 | |
{ | |
Vec3 pos_; | |
QMatrix4x4 transform_; | |
}; | |
struct BasicVert | |
{ | |
protected: | |
Vec3 pos_; | |
Vec2 uv_; | |
public: | |
BasicVert(const Vec3& pos) : pos_(pos) {} | |
float x() const { return pos_.x(); } | |
float y() const { return pos_.y(); } | |
float z() const { return pos_.z(); } | |
}; | |
template<typename VertT> | |
struct Shader | |
{ | |
using vert_t = VertT; | |
std::string name_; | |
std::string vs_code_; | |
std::string fs_colde_; | |
std::string basecolor_; | |
}; | |
template<typename ShaderT> | |
struct Surf | |
{ | |
protected: | |
using vert_t = typename ShaderT::vert_t; | |
using shader_t = ShaderT; | |
std::tuple<int, int, int> indices_; | |
std::shared_ptr<ShaderT> shader_; | |
}; | |
template<typename ShaderT> | |
struct Mesh | |
{ | |
using surf_t = Surf<ShaderT>; | |
using vert_t = typename Surf<ShaderT>::vert_t; | |
Placement placement_; | |
std::vector<vert_t> verts_; | |
std::vector<surf_t> surfs_; | |
}; | |
using BasicMesh = Mesh<Shader<BasicVert>>; | |
struct PointLight | |
{ | |
Placement placement_; | |
}; | |
struct BasicScene | |
{ | |
std::vector<BasicMesh> meshes_; | |
std::vector<PointLight> lights_; | |
}; | |
} | |
/** ====================== | |
ATTRIBUTES, DATA | |
========================= | |
*/ | |
template<typename T> | |
struct GLTypeMap {}; | |
template<> struct GLTypeMap<float> { const static short glt = GL_FLOAT; }; | |
template<> struct GLTypeMap<int> { const static short glt = GL_INT; }; | |
using VattrCallback = std::function<void( | |
const int& offset, const void* data, const int size, | |
const QString& name, const short& gltype, const int& dim, const int& stride)>; | |
template<typename T, int dimension> | |
struct VertAttr | |
{ | |
protected: | |
int dim_ = dimension; | |
std::vector<T> data_; | |
QString name_; | |
int offset_; | |
short stride_ = 0; | |
public: | |
// Setters | |
void set_verts(const int& offset, const QString& name, const std::vector<T>& verts) | |
{ | |
this->offset_ = offset; | |
this->name_ = name; | |
this->data_ = verts; | |
} | |
// Getters | |
const T* data() const { return this->data_.data(); } | |
int dim() const { return this->dim_; } | |
short gltype() const { return GLTypeMap<T>::glt; } | |
int count() const { return this->data_.size(); } | |
int bytesize() const { return this->count() * sizeof(T); } | |
int b_offset() const { return this->offset_; } | |
int b_end() const { return this->b_offset() + bytesize(); } | |
std::string name() const { return this->name_.toStdString(); } | |
void dump_self(const VattrCallback& func) const | |
{ | |
func(b_offset(), (void*)data(), bytesize(), name_, gltype(), dim(), stride_); | |
} | |
}; | |
template<int size> | |
struct Attribute { | |
std::string name_; | |
int size_ = size; | |
}; | |
struct Uniform { | |
std::string name_; | |
}; | |
// INPUTDATA ==== | |
/** | |
OpenGL のインターフェイスに依存しない、形状のみを格納する構造体。 | |
*/ | |
struct InputData | |
{ | |
InputData() {} | |
QString vs_; | |
QString fs_; | |
QImage texture_; | |
VertAttr<float, 3> p_; | |
VertAttr<float, 3> cd_; | |
VertAttr<float, 2> uv_; | |
Attribute<1> frame_; | |
Uniform mvp_; | |
int vertnum() const | |
{ | |
return p_.count() / 3; | |
} | |
int bytesize() const | |
{ | |
return this->p_.bytesize() + this->cd_.bytesize() + this->uv_.bytesize(); | |
} | |
void iter_vattr(const VattrCallback& func) const | |
{ | |
p_.dump_self(func); | |
cd_.dump_self(func); | |
uv_.dump_self(func); | |
} | |
}; | |
/** | |
Create a basic cube | |
*/ | |
inline std::shared_ptr<InputData> basic_obj() | |
{ | |
InputData id; | |
// Shaders | |
{ | |
id.vs_ = R"( | |
attribute vec3 vP; | |
attribute vec3 vCd; | |
attribute vec2 vUv; | |
attribute float frame; | |
uniform mat4 mvpMat; | |
out float varFrame; | |
out vec3 fCd; | |
out vec2 fUv; | |
void main(void) { | |
fCd = vCd; | |
fUv = vUv; | |
varFrame = frame; | |
gl_Position = mvpMat * vec4(vP, 1.0); | |
} | |
)"; | |
id.fs_ = R"(#version 330 | |
layout(location = 0, index = 0) out vec4 fragColor; | |
in float varFrame; | |
in vec3 fCd; | |
in vec2 fUv; | |
uniform sampler2D sampler; | |
void main(void) { | |
float t = varFrame * 5; | |
fragColor = vec4( | |
abs(cos(t/100.0)), | |
abs(cos(t/200.0)), | |
abs(cos(t/300.0)), | |
1.0) * vec4(fCd, 1.0) + 0.3; | |
fragColor = vec4(texture( sampler, fUv).rg, 0.5, 1); | |
} | |
)"; | |
} | |
// Verts | |
{ | |
id.p_.set_verts(0, | |
"vP", { | |
-1.0f,-1.0f,-1.0f, -1.0f,-1.0f, 1.0f, -1.0f, 1.0f, 1.0f, | |
1.0f, 1.0f,-1.0f, -1.0f,-1.0f,-1.0f, -1.0f, 1.0f,-1.0f, | |
1.0f,-1.0f, 1.0f, -1.0f,-1.0f,-1.0f, 1.0f,-1.0f,-1.0f, | |
1.0f, 1.0f,-1.0f, 1.0f,-1.0f,-1.0f, -1.0f,-1.0f,-1.0f, | |
-1.0f,-1.0f,-1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f,-1.0f, | |
1.0f,-1.0f, 1.0f, -1.0f,-1.0f, 1.0f, -1.0f,-1.0f,-1.0f, | |
-1.0f, 1.0f, 1.0f, -1.0f,-1.0f, 1.0f, 1.0f,-1.0f, 1.0f, | |
1.0f, 1.0f, 1.0f, 1.0f,-1.0f,-1.0f, 1.0f, 1.0f,-1.0f, | |
1.0f,-1.0f,-1.0f, 1.0f, 1.0f, 1.0f, 1.0f,-1.0f, 1.0f, | |
1.0f, 1.0f, 1.0f, 1.0f, 1.0f,-1.0f, -1.0f, 1.0f,-1.0f, | |
1.0f, 1.0f, 1.0f, -1.0f, 1.0f,-1.0f, -1.0f, 1.0f, 1.0f, | |
1.0f, 1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 1.0f,-1.0f, 1.0f | |
}); | |
id.cd_.set_verts(id.p_.b_end(), | |
"vCd", { | |
0.583f, 0.771f, 0.014f, 0.609f, 0.115f, 0.436f, 0.327f, 0.483f, 0.844f, | |
0.822f, 0.569f, 0.201f, 0.435f, 0.602f, 0.223f, 0.310f, 0.747f, 0.185f, | |
0.597f, 0.770f, 0.761f, 0.559f, 0.436f, 0.730f, 0.359f, 0.583f, 0.152f, | |
0.483f, 0.596f, 0.789f, 0.559f, 0.861f, 0.639f, 0.195f, 0.548f, 0.859f, | |
0.014f, 0.184f, 0.576f, 0.771f, 0.328f, 0.970f, 0.406f, 0.615f, 0.116f, | |
0.676f, 0.977f, 0.133f, 0.971f, 0.572f, 0.833f, 0.140f, 0.616f, 0.489f, | |
0.997f, 0.513f, 0.064f, 0.945f, 0.719f, 0.592f, 0.543f, 0.021f, 0.978f, | |
0.279f, 0.317f, 0.505f, 0.167f, 0.620f, 0.077f, 0.347f, 0.857f, 0.137f, | |
0.055f, 0.953f, 0.042f, 0.714f, 0.505f, 0.345f, 0.783f, 0.290f, 0.734f, | |
0.722f, 0.645f, 0.174f, 0.302f, 0.455f, 0.848f, 0.225f, 0.587f, 0.040f, | |
0.517f, 0.713f, 0.338f, 0.053f, 0.959f, 0.120f, 0.393f, 0.621f, 0.362f, | |
0.673f, 0.211f, 0.457f, 0.820f, 0.883f, 0.371f, 0.982f, 0.099f, 0.879f | |
}); | |
id.uv_.set_verts(id.cd_.b_end(), | |
"vUv", { | |
1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, | |
1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, | |
0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, | |
1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, | |
1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, | |
0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, | |
1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, | |
1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, | |
0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, | |
1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, | |
1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, | |
0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, | |
}); | |
} | |
// Attribs | |
{ | |
id.frame_.name_ = "frame"; | |
id.frame_.size_ = 1; | |
id.mvp_.name_ = "mvpMat"; | |
} | |
// Textures | |
{ | |
id.texture_ = QImage(QString(":/qtglt/textures/side1.png")); | |
} | |
return std::make_shared<InputData>(id); | |
} | |
template<typename UData> | |
struct OglData | |
{ | |
using AttriibLocation = int; | |
using UniformLocation = int; | |
QOpenGLShaderProgram program_; | |
QOpenGLBuffer vbo_{ QOpenGLBuffer::VertexBuffer }; | |
QOpenGLBuffer ibo_; | |
QOpenGLVertexArrayObject vao_; | |
QOpenGLTexture* texture_; | |
AttriibLocation frame_; | |
UniformLocation mvp_; | |
std::shared_ptr<UData> udata_; | |
OglData() | |
{ | |
vao_.create(); | |
vbo_.create(); | |
} | |
void bind() | |
{ | |
vao_.bind(); | |
vbo_.bind(); | |
program_.bind(); | |
texture_->bind(); | |
} | |
void release() | |
{ | |
vao_.release(); | |
vbo_.release(); | |
program_.release(); | |
texture_->release(); | |
} | |
int offset() const | |
{ | |
return 0; | |
} | |
inline void make(std::shared_ptr<UData> udata) | |
{ | |
udata_ = udata; | |
const auto& ud = *(udata_.get()); | |
// Shader | |
program_.addShaderFromSourceCode(QOpenGLShader::Vertex, ud.vs_); | |
program_.addShaderFromSourceCode(QOpenGLShader::Fragment, ud.fs_); | |
program_.link(); | |
// Vertex | |
vbo_.create(); | |
vbo_.bind(); | |
vbo_.setUsagePattern(QOpenGLBuffer::StaticDraw); | |
vbo_.allocate(ud.bytesize()); | |
ud.iter_vattr([&]( | |
const int& offset, const void* data, const int size, | |
const QString& name, const short& gltype, const int& dim, const int& stride) | |
{ | |
vbo_.write(offset, data, size); | |
program_.setAttributeBuffer(name.toStdString().c_str(), gltype, offset, dim, stride); | |
program_.enableAttributeArray(name.toStdString().c_str()); | |
}); | |
// Attributes | |
frame_ = program_.attributeLocation(ud.frame_.name_.c_str()); | |
mvp_ = program_.uniformLocation(ud.mvp_.name_.c_str()); | |
texture_ = new QOpenGLTexture(ud.texture_.mirrored()); | |
texture_->setMinificationFilter(QOpenGLTexture::LinearMipMapLinear); | |
texture_->setMagnificationFilter(QOpenGLTexture::Linear); | |
} | |
}; | |
/** ====================== | |
Mouse, User Input, OS, things are dependent on user environment. | |
========================= | |
*/ | |
struct Environ | |
{ | |
private: | |
float frame_ = 0.0f; | |
public: | |
bool is_mouse_pressed_ = false; | |
Vec2 mouse_dir_; | |
QPoint last_mouse_pos_; | |
UserCommand last_user_command_ = UserCommand::kNone; | |
void tick() | |
{ | |
frame_++; | |
} | |
float frame() const { return frame_; } | |
}; | |
/** ====================== | |
APP | |
========================= | |
*/ | |
class qtglt : public QOpenGLWidget | |
{ | |
Q_OBJECT | |
private: | |
QTimer timer_; | |
QOpenGLContext context_; | |
Environ environ_; | |
Camera cam_; | |
QString msg_ = ""; | |
OglData<InputData> main_buffers; | |
//OglData normal_line_buffers; | |
public: | |
qtglt(QWidget* parent = nullptr) : QOpenGLWidget(parent), context_(0) | |
{ | |
connect(&timer_, SIGNAL(timeout()), this, SLOT(update())); | |
timer_.start(16.666666); | |
} | |
virtual void initializeGL() | |
{ | |
glEnable(GL_DEPTH_TEST); | |
glDepthFunc(GL_LESS); | |
// Vertex | |
{ | |
main_buffers.make(basic_obj()); | |
} | |
} | |
virtual void paintGL() | |
{ | |
// do user control things. | |
{ | |
environ_.tick(); | |
handle_user_command(); | |
cam_.update(); | |
} | |
{ | |
main_buffers.bind(); | |
main_buffers.program_.setAttributeValue(main_buffers.frame_, environ_.frame()); | |
main_buffers.program_.setUniformValue(main_buffers.mvp_, cam_.mvp()); | |
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); | |
glDrawArrays(GL_TRIANGLES, main_buffers.offset(), main_buffers.udata_->vertnum()); | |
main_buffers.release(); | |
} | |
{ | |
} | |
} | |
virtual void resizeGL(int w, int h) | |
{ | |
cam_.aspect_ratio_ = size(); | |
glViewport(0, 0, w, h); | |
} | |
// GL DELEGATE END ==== | |
void handle_error_msg() const | |
{ | |
if (msg_.size() > 0) { | |
qWarning() << this->msg_; | |
std::ofstream ofst(R"(d:/temps/te.log)"); | |
ofst << this->msg_.toStdString(); | |
ofst.close(); | |
throw 3; | |
} | |
} | |
void handle_user_command() | |
{ | |
// Mouse Control Handlers | |
const QPoint& mouse_pos = this->mapFromGlobal(QCursor::pos()); | |
QPoint mov = (environ_.last_mouse_pos_ - mouse_pos); | |
environ_.mouse_dir_.setX(float(mov.x()) / size().width()); | |
environ_.mouse_dir_.setY(float(mov.y()) / size().height()); | |
environ_.last_mouse_pos_ = mouse_pos; | |
if (environ_.is_mouse_pressed_ && (environ_.mouse_dir_.length() != 0)) { | |
qInfo() << environ_.mouse_dir_.x() << environ_.mouse_dir_.y(); | |
cam_.rotate_around_targer(environ_.mouse_dir_.x() * 50); | |
} | |
// User Command Handlers | |
switch (environ_.last_user_command_) | |
{ | |
case UserCommand::kCamDollyIn: | |
cam_.translate(cam_.forward()); | |
break; | |
case UserCommand::kCamTruckLeft: | |
cam_.translate(cam_.left()); | |
break; | |
case UserCommand::kCamTruckRight: | |
cam_.translate(cam_.right()); | |
break; | |
case UserCommand::kCamDollyOut: | |
cam_.translate(cam_.back()); | |
break; | |
case UserCommand::kCamPanLeft: | |
//cam_.rotate(10); | |
cam_.rotate_around_targer(10); | |
break; | |
case UserCommand::kCamPanRight: | |
//cam_.rotate(-10); | |
cam_.rotate_around_targer(-10); | |
break; | |
case UserCommand::kCamFocusOrigin: | |
cam_.target_ = Vec3(0, 0, 0); | |
default: | |
break; | |
} | |
environ_.last_user_command_ = UserCommand::kNone; | |
} | |
void keyPressEvent(QKeyEvent* e) override | |
{ | |
switch (e->key()) | |
{ | |
case Qt::Key_W: | |
case Qt::Key_Up: | |
environ_.last_user_command_ = UserCommand::kCamDollyIn; | |
return; | |
case Qt::Key_A: | |
case Qt::Key_Left: | |
environ_.last_user_command_ = UserCommand::kCamTruckLeft; | |
return; | |
case Qt::Key_D: | |
case Qt::Key_Right: | |
environ_.last_user_command_ = UserCommand::kCamTruckRight; | |
return; | |
case Qt::Key_S: | |
case Qt::Key_Down: | |
environ_.last_user_command_ = UserCommand::kCamDollyOut; | |
return; | |
case Qt::Key_Q: | |
environ_.last_user_command_ = UserCommand::kCamPanLeft; | |
return; | |
case Qt::Key_E: | |
environ_.last_user_command_ = UserCommand::kCamPanRight; | |
return; | |
case Qt::Key_R: | |
environ_.last_user_command_ = UserCommand::kCamFocusOrigin; | |
return; | |
default: | |
break; | |
} | |
} | |
void mousePressEvent(QMouseEvent* mev) | |
{ | |
environ_.is_mouse_pressed_ = true; | |
} | |
void mouseReleaseEvent(QMouseEvent* mev) | |
{ | |
environ_.is_mouse_pressed_ = false; | |
} | |
void mouse_dragged(const Vec2& mov) | |
{ | |
qInfo() << mov.x() << mov.y(); | |
} | |
void check_ok(bool result, const QString& msg) | |
{ | |
if (!result) { | |
this->msg_ += msg; | |
} | |
} | |
}; | |
inline int call_me_in_main(int argc, char *argv[]) | |
{ | |
QApplication a(argc, argv); | |
qtglt mygl; | |
mygl.show(); | |
return a.exec(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment