【开发日志】2022.09.09 ZENO viewportwidge---raycast---sphere&AABB Box(zry还没把一些基础交互加上去,暂时不能用OBB)

RAYCAST

vdoc.pub_game-physics-cookbook.pdf

【开发日志】2022.09.09 ZENO viewportwidge---raycast---sphere&AABB Box(zry还没把一些基础交互加上去,暂时不能用OBB)_第1张图片


Linetest  Sphere

【开发日志】2022.09.09 ZENO viewportwidge---raycast---sphere&AABB Box(zry还没把一些基础交互加上去,暂时不能用OBB)_第2张图片

 【开发日志】2022.09.09 ZENO viewportwidge---raycast---sphere&AABB Box(zry还没把一些基础交互加上去,暂时不能用OBB)_第3张图片

【开发日志】2022.09.09 ZENO viewportwidge---raycast---sphere&AABB Box(zry还没把一些基础交互加上去,暂时不能用OBB)_第4张图片  

bool Raycast(const Sphere& sphere, const Ray& ray, RaycastResult* outResult) {
	ResetRaycastResult(outResult);

	vec3 e = sphere.position - ray.origin;
	float rSq = sphere.radius * sphere.radius;

	float eSq = MagnitudeSq(e);
	float a = Dot(e, ray.direction); // ray.direction is assumed to be normalized
	float bSq = /*sqrtf(*/eSq - (a * a)/*)*/;
	float f = sqrt(fabsf((rSq)- /*(b * b)*/bSq));

	// Assume normal intersection!
	float t = a - f;

	// No collision has happened
	if (rSq - (eSq - a * a) < 0.0f) {
		return false;
	}
	// Ray starts inside the sphere
	else if (eSq < rSq) {
		// Just reverse direction
		t = a + f;
	}
	if (outResult != 0) {
		outResult->t = t;
		outResult->hit = true;
		outResult->point = ray.origin + ray.direction * t;
		outResult->normal = Normalized(outResult->point - sphere.position);
	}
	return true;
}
bool Linetest(const Sphere& sphere, const Line& line) {
	Point closest = ClosestPoint(line, sphere.position);
	float distSq = MagnitudeSq(sphere.position - closest);
	return distSq <= (sphere.radius * sphere.radius);
}

ZENO

static std::optional ray_sphere_intersect(
    zeno::vec3f const &ray_pos,
    zeno::vec3f const &ray_dir,
    zeno::vec3f const &sphere_center,
    float sphere_radius
) {
    auto &p = ray_pos;
    auto &d = ray_dir;
    auto &c = sphere_center;
    auto &r = sphere_radius;
    float t = zeno::dot(c - p, d);
    if (t < 0) {
        return std::nullopt;
    }
    zeno::vec3f dist = t * d + p - c;
    float l = zeno::length(dist);
    if (l > r) {
        return std::nullopt;
    }
    float t_diff = std::sqrt(r * r - l * l);
    float final_t = t - t_diff;
    return final_t;
}
if (zeno::objectGetFocusCenterRadius(ptr, center, radius)) {
    if (auto ret = ray_sphere_intersect(ro, rd, center, radius)) {
        float t = *ret;
        if (t < min_t) {
            min_t = t;
            name = key;
        }
    }
}
objectGetFocusCenterRadius()在ObjectGeometryInfo.cpp 里,见后文 

【开发日志】2022.09.09 ZENO viewportwidge---raycast---sphere&AABB Box(zry还没把一些基础交互加上去,暂时不能用OBB)_第5张图片

【开发日志】2022.09.09 ZENO viewportwidge---raycast---sphere&AABB Box(zry还没把一些基础交互加上去,暂时不能用OBB)_第6张图片

 

bool Raycast(const AABB& aabb, const Ray& ray, RaycastResult* outResult) {
	ResetRaycastResult(outResult);

	vec3 min = GetMin(aabb);
	vec3 max = GetMax(aabb);

	// Any component of direction could be 0!
	// Address this by using a small number, close to
	// 0 in case any of directions components are 0
	float t1 = (min.x - ray.origin.x) / (CMP(ray.direction.x, 0.0f) ? 0.00001f : ray.direction.x);
	float t2 = (max.x - ray.origin.x) / (CMP(ray.direction.x, 0.0f) ? 0.00001f : ray.direction.x);
	float t3 = (min.y - ray.origin.y) / (CMP(ray.direction.y, 0.0f) ? 0.00001f : ray.direction.y);
	float t4 = (max.y - ray.origin.y) / (CMP(ray.direction.y, 0.0f) ? 0.00001f : ray.direction.y);
	float t5 = (min.z - ray.origin.z) / (CMP(ray.direction.z, 0.0f) ? 0.00001f : ray.direction.z);
	float t6 = (max.z - ray.origin.z) / (CMP(ray.direction.z, 0.0f) ? 0.00001f : ray.direction.z);

	float tmin = fmaxf(fmaxf(fminf(t1, t2), fminf(t3, t4)), fminf(t5, t6));
	float tmax = fminf(fminf(fmaxf(t1, t2), fmaxf(t3, t4)), fmaxf(t5, t6));

	// if tmax < 0, ray is intersecting AABB
	// but entire AABB is behing it's origin
	if (tmax < 0) {
		return false;
	}

	// if tmin > tmax, ray doesn't intersect AABB
	if (tmin > tmax) {
		return false;
	}

	float t_result = tmin;

	// If tmin is < 0, tmax is closer
	if (tmin < 0.0f) {
		t_result = tmax;
	}

	if (outResult != 0) {
		outResult->t = t_result;
		outResult->hit = true;
		outResult->point = ray.origin + ray.direction * t_result;

		vec3 normals[] = {
			vec3(-1, 0, 0),
			vec3(1, 0, 0),
			vec3(0, -1, 0),
			vec3(0, 1, 0),
			vec3(0, 0, -1),
			vec3(0, 0, 1)
		};
		float t[] = { t1, t2, t3, t4, t5, t6 };

		for (int i = 0; i < 6; ++i) {
			if (CMP(t_result, t[i])) {
				outResult->normal = normals[i];
			}
		}
	}

	return true;
}
bool Linetest(const AABB& aabb, const Line& line) {
	Ray ray;
	ray.origin = line.start;
	ray.direction = Normalized(line.end - line.start);
	RaycastResult raycast;
	if (!Raycast(aabb, ray, &raycast)) {
		return false;
	}
	float t = raycast.t;

	return t >= 0 && t * t <= LengthSq(line);
}

ZENO 

static std::optional ray_box_intersect(
    zeno::vec3f const &bmin,
    zeno::vec3f const &bmax,
    zeno::vec3f const &ray_pos,
    zeno::vec3f const &ray_dir
) {
    //objectGetBoundingBox(IObject *ptr, vec3f &bmin, vec3f &bmax);

    auto &min = bmin;
    auto &max = bmax;
    auto &p = ray_pos;
    auto &d = ray_dir;
    //auto &t = t;

    float t1 = (min[0] - p[0]) / (CMP(d[0], 0.0f) ? 0.00001f : d[0]);
    float t2 = (max[0] - p[0]) / (CMP(d[0], 0.0f) ? 0.00001f : d[0]);
    float t3 = (min[1] - p[1]) / (CMP(d[1], 0.0f) ? 0.00001f : d[1]);
    float t4 = (max[1] - p[1]) / (CMP(d[1], 0.0f) ? 0.00001f : d[1]);
    float t5 = (min[2] - p[2]) / (CMP(d[2], 0.0f) ? 0.00001f : d[2]);
    float t6 = (max[2] - p[2]) / (CMP(d[2], 0.0f) ? 0.00001f : d[2]);

    float tmin = fmaxf(fmaxf(fminf(t1, t2), fminf(t3, t4)), fminf(t5, t6));
    float tmax = fminf(fminf(fmaxf(t1, t2), fmaxf(t3, t4)), fmaxf(t5, t6));

    // if tmax < 0, ray is intersecting AABB
    // but entire AABB is behing it's origin
    if (tmax < 0) {
        return std::nullopt;
    }

    // if tmin > tmax, ray doesn't intersect AABB
    if (tmin > tmax) {
        return std::nullopt;
    }

    float t_result = tmin;

    // If tmin is < 0, tmax is closer
    if (tmin < 0.0f) {
        t_result = tmax;
    }
    //zeno::vec3f  final_t = p + d * t_result;
    return t_result;
}
zeno::vec3f bmin,bmax;
    if (zeno::objectGetBoundingBox(ptr,bmin,bmax) ){
        if (auto ret = ray_box_intersect(bmin, bmax, ro, rd)) {
            float t = *ret;
            if (t < min_t) {
                min_t = t;
                name = key;
            }
        }
    }
}

ObjectGeometryInfo.cpp 

/zeno/zeno/src/funcs

#include 
#include 
#include 
#include 

namespace zeno {

ZENO_API bool objectGetBoundingBox(IObject *ptr, vec3f &bmin, vec3f &bmax) {
    auto &ud = ptr->userData();
    if (ud.has("_bboxMin") && ud.has("_bboxMax")) {
        bmin = ud.getLiterial("_bboxMin");
        bmax = ud.getLiterial("_bboxMax");
        return true;
    } else {
        if (auto obj = dynamic_cast(ptr)) {
            std::tie(bmin, bmax) = primBoundingBox(obj);
            ud.setLiterial("_bboxMin", bmin);
            ud.setLiterial("_bboxMax", bmax);
            return true;
        }
        else {
            return false;
        }
    }
}


ZENO_API bool objectGetFocusCenterRadius(IObject *ptr, vec3f ¢er, float &radius) {
    vec3f bmin, bmax;
    if (!objectGetBoundingBox(ptr, bmin, bmax))
        return false;
    auto delta = bmax - bmin;
    radius = std::max(std::max(delta[0], delta[1]), delta[2]) * 0.5f;
    center = (bmin + bmax) * 0.5f;
    return true;
}

}
for (auto const &[key, ptr] : scene->objectsMan->pairs()) {
    zeno::vec3f center;
    float radius;
    zeno::vec3f ro(cam_pos[0], cam_pos[1], cam_pos[2]);
    zeno::vec3f rd(rdir[0], rdir[1], rdir[2]);
    if (zeno::objectGetFocusCenterRadius(ptr, center, radius)) {
        if (auto ret = ray_sphere_intersect(ro, rd, center, radius)) {
            float t = *ret;
            if (t < min_t) {
                min_t = t;
                name = key;
            }
        }
    }
}

 


viewportwidget.cpp

/zeno/ui/zenoedit/viewport

#include 
#include "viewportwidget.h"
#include "zenovis.h"
#include "camerakeyframe.h"
#include "timeline/ztimeline.h"
#include "graphsmanagment.h"
#include "model/graphsmodel.h"
#include "launch/corelaunch.h"
#include "zenoapplication.h"
#include "zenomainwindow.h"
#include "dialog/zrecorddlg.h"
#include "dialog/zrecprogressdlg.h"
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
//#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "recordvideomgr.h"
#define CMP(x, y) \
	(fabsf(x - y) <= FLT_EPSILON * fmaxf(1.0f, fmaxf(fabsf(x), fabsf(y))))

static std::optional ray_sphere_intersect(
    zeno::vec3f const &ray_pos,
    zeno::vec3f const &ray_dir,
    zeno::vec3f const &sphere_center,
    float sphere_radius
) {
    auto &p = ray_pos;
    auto &d = ray_dir;
    auto &c = sphere_center;
    auto &r = sphere_radius;
    float t = zeno::dot(c - p, d);
    if (t < 0) {
        return std::nullopt;
    }
    zeno::vec3f dist = t * d + p - c;
    float l = zeno::length(dist);
    if (l > r) {
        return std::nullopt;
    }
    float t_diff = std::sqrt(r * r - l * l);
    float final_t = t - t_diff;
    return final_t;
}

static std::optional ray_box_intersect(
    zeno::vec3f const &bmin,
    zeno::vec3f const &bmax,
    zeno::vec3f const &ray_pos,
    zeno::vec3f const &ray_dir
) {
    //objectGetBoundingBox(IObject *ptr, vec3f &bmin, vec3f &bmax);

    auto &min = bmin;
    auto &max = bmax;
    auto &p = ray_pos;
    auto &d = ray_dir;
    //auto &t = t;

    float t1 = (min[0] - p[0]) / (CMP(d[0], 0.0f) ? 0.00001f : d[0]);
    float t2 = (max[0] - p[0]) / (CMP(d[0], 0.0f) ? 0.00001f : d[0]);
    float t3 = (min[1] - p[1]) / (CMP(d[1], 0.0f) ? 0.00001f : d[1]);
    float t4 = (max[1] - p[1]) / (CMP(d[1], 0.0f) ? 0.00001f : d[1]);
    float t5 = (min[2] - p[2]) / (CMP(d[2], 0.0f) ? 0.00001f : d[2]);
    float t6 = (max[2] - p[2]) / (CMP(d[2], 0.0f) ? 0.00001f : d[2]);

    float tmin = fmaxf(fmaxf(fminf(t1, t2), fminf(t3, t4)), fminf(t5, t6));
    float tmax = fminf(fminf(fmaxf(t1, t2), fmaxf(t3, t4)), fmaxf(t5, t6));

    // if tmax < 0, ray is intersecting AABB
    // but entire AABB is behing it's origin
    if (tmax < 0) {
        return std::nullopt;
    }

    // if tmin > tmax, ray doesn't intersect AABB
    if (tmin > tmax) {
        return std::nullopt;
    }

    float t_result = tmin;

    // If tmin is < 0, tmax is closer
    if (tmin < 0.0f) {
        t_result = tmax;
    }
    //zeno::vec3f  final_t = p + d * t_result;
    return t_result;
}


static bool test_in_selected_bounding(
    QVector3D centerWS,
    QVector3D cam_posWS,
    QVector3D left_normWS,
    QVector3D right_normWS,
    QVector3D up_normWS,
    QVector3D down_normWS
) {
    QVector3D dir =  centerWS - cam_posWS;
    dir.normalize();
    bool left_test = QVector3D::dotProduct(dir, left_normWS) > 0;
    bool right_test = QVector3D::dotProduct(dir, right_normWS) > 0;
    bool up_test = QVector3D::dotProduct(dir, up_normWS) > 0;
    bool down_test = QVector3D::dotProduct(dir, down_normWS) > 0;
    return left_test && right_test && up_test && down_test;
}

CameraControl::CameraControl(QWidget* parent)
    : m_mmb_pressed(false)
    , m_theta(0.)
    , m_phi(0.)
    , m_ortho_mode(false)
    , m_fov(45.)
    , m_radius(5.0)
    , m_res(1, 1)
    , m_aperture(0.1f)
    , m_focalPlaneDistance(2.0f)
{
    transformer = std::make_unique();
    updatePerspective();
}

void CameraControl::setRes(QVector2D res)
{
    m_res = res;
}

void CameraControl::setAperture(float aperture){
    m_aperture = aperture;
}
void CameraControl::setDisPlane(float disPlane){
    m_focalPlaneDistance = disPlane;
}

void CameraControl::fakeMousePressEvent(QMouseEvent* event)
{
    if (event->buttons() & Qt::MiddleButton) {
        m_lastPos = event->pos();
    }
    else if (event->buttons() & Qt::LeftButton) {
        m_boundRectStartPos = event->pos();
        m_lastMovePos = event->pos();
        // check if clicked a selected object
        auto scene = Zenovis::GetInstance().getSession()->get_scene();
        if (!scene->selected.empty() && mouseEnteredRing(event->x(), event->y())) {
            transformer->startTransform();
        }
    }
}

QVector2D CameraControl::qtCoordToGLCoord(int x, int y) {
    auto w = res()[0];
    auto h = res()[1];
    auto mx = x * 1.0f;
    auto my = y * 1.0f;

    mx = (2 * mx / w) - 1;
    my = 1 - (2 * my / h);
    return {mx, my};
}

bool CameraControl::mouseEnteredRing(int x, int y) {
    auto scene = Zenovis::GetInstance().getSession()->get_scene();
    auto world_coord = glm::vec4(transformer->getCenter(), 1.0);
    auto screen_coord = scene->camera->m_proj * scene->camera->m_view * world_coord;
    screen_coord /= screen_coord[3];

    auto cx = screen_coord[0];
    auto cy = screen_coord[1];

    auto mp = qtCoordToGLCoord(x, y);
    auto mx = mp.x();
    auto my = mp.y();

    auto ar = res()[0] / res()[1];

    auto dis = sqrt(((cx - mx) * ar) * ((cx - mx) * ar) + (cy - my) * (cy - my));
    // 0.1 is the bigger radius of ring
    return dis < 0.1;
}

void CameraControl::fakeMouseMoveEvent(QMouseEvent* event)
{
    auto session = Zenovis::GetInstance().getSession();
    auto scene = session->get_scene();

    if (event->buttons() & Qt::MiddleButton) {
        float ratio = QApplication::desktop()->devicePixelRatio();
        float xpos = event->x(), ypos = event->y();
        float dx = xpos - m_lastPos.x(), dy = ypos - m_lastPos.y();
        dx *= ratio / m_res[0];
        dy *= ratio / m_res[1];
        bool shift_pressed = event->modifiers() & Qt::ShiftModifier;
        if (shift_pressed)
        {
            float cos_t = cos(m_theta);
            float sin_t = sin(m_theta);
            float cos_p = cos(m_phi);
            float sin_p = sin(m_phi);
            QVector3D back(cos_t * sin_p, sin_t, -cos_t * cos_p);
            QVector3D up(-sin_t * sin_p, cos_t, sin_t * cos_p);
            QVector3D right = QVector3D::crossProduct(up, back);
            up = QVector3D::crossProduct(back, right);
            right.normalize();
            up.normalize();
            QVector3D delta = right * dx + up * dy;
            m_center += delta * m_radius;
        }
        else
        {
            m_theta -= dy * M_PI;
            m_phi += dx * M_PI;
        }
        m_lastPos = QPointF(xpos, ypos);
    }
    else if (event->buttons() & Qt::LeftButton) {
        if (transformer->isTransforming()) {
            bool alt_pressed = event->modifiers() & Qt::AltModifier;
            bool ctrl_pressed = event->modifiers() & Qt::ControlModifier;
            glm::mat4 transform_matrix(1.0f);

            auto transform_center = transformer->getCenter();
            QVector3D center_qvec(transform_center[0], transform_center[1], transform_center[2]);
            zeno::vec3f center_zvec(transform_center[0], transform_center[1], transform_center[2]);

            std::set> interactingGraphics;
            QVector3D x_axis(1, 0, 0);
            QVector3D y_axis(0, 1, 0);
            QVector3D z_axis(0, 0, 1);

            QVariant start, end;
            QVector3D start_vec, end_vec;
            // get transform matrix
            if (ctrl_pressed && alt_pressed) {
                // rotate
                if (m_pressedKeys.contains(Qt::Key_Z)) {
                    // rotate along x=center_qvec.x plane
                    start = hitOnPlane((float)m_lastMovePos.x() / res().x(), (float)m_lastMovePos.y() / res().y(),
                                       x_axis, center_qvec);
                    end = hitOnPlane((float)event->x() / res().x(), (float)event->y() / res().y(),
                                     x_axis, center_qvec);
                    if (start.isValid() && end.isValid()) {
                        start_vec = start.value() - center_qvec;
                        end_vec = end.value() - center_qvec;
                        transformer->rotate(start_vec, end_vec, x_axis);
                        interactingGraphics.insert(
                            zenovis::makeGraphicInteractingAxis(scene, center_zvec, {1, 0, 0}));
                    }
                }
                else if (m_pressedKeys.contains(Qt::Key_X)) {
                    // rotate along y=center_qvec.y plane
                    start = hitOnPlane((float)m_lastMovePos.x() / res().x(), (float)m_lastMovePos.y() / res().y(),
                                       y_axis, center_qvec);
                    end = hitOnPlane((float)event->x() / res().x(), (float)event->y() / res().y(),
                                     y_axis, center_qvec);
                    if (start.isValid() && end.isValid()) {
                        start_vec = start.value() - center_qvec;
                        end_vec = end.value() - center_qvec;
                        transformer->rotate(start_vec, end_vec, y_axis);
                        interactingGraphics.insert(
                            zenovis::makeGraphicInteractingAxis(scene, center_zvec, {0, 1, 0}));
                    }
                }
                else if (m_pressedKeys.contains(Qt::Key_C)) {
                    // rotate along z=center_qvec.z plane
                    start = hitOnPlane((float)m_lastMovePos.x() / res().x(), (float)m_lastMovePos.y() / res().y(),
                                       z_axis, center_qvec);
                    end = hitOnPlane((float)event->x() / res().x(), (float)event->y() / res().y(),
                                     z_axis, center_qvec);
                    if (start.isValid() && end.isValid()) {
                        start_vec = start.value() - center_qvec;
                        end_vec = end.value() - center_qvec;
                        transformer->rotate(start_vec, end_vec, z_axis);
                        interactingGraphics.insert(
                            zenovis::makeGraphicInteractingAxis(scene, center_zvec, {0, 0, 1}));
                    }
                }
            }
            else if (alt_pressed) {
                // scale
                auto vp = scene->camera->m_proj * scene->camera->m_view;
                auto screen_center = vp * glm::vec4(transform_center, 1.0f);
                screen_center /= screen_center[3];
                QVector2D screen_center_2d(screen_center[0], screen_center[1]);
                QVector2D mouse_pos = qtCoordToGLCoord(event->x(), event->y());
                float scale_size = mouse_pos.distanceToPoint(screen_center_2d);

                if (m_pressedKeys.contains(Qt::Key_A)) {
                    // scale along a plane
                    if (m_pressedKeys.contains(Qt::Key_Z)) {
                        // scale along x=center_qvec.x plane
                        auto cur = hitOnPlane((float)event->x() / res().x(), (float)event->y() / res().y(),
                                              y_axis, center_qvec);
                        if (cur.isValid()) {
                            transformer->scale(cur.value() - center_qvec,{0, 1, 1}, scale_size);
                        }
                        interactingGraphics.insert(
                            zenovis::makeGraphicInteractingAxis(scene, center_zvec,{0, 1, 1}));
                    }
                    else if (m_pressedKeys.contains(Qt::Key_X)) {
                        // scale along y=center_qvec.y plane
                        auto cur = hitOnPlane((float)event->x() / res().x(), (float)event->y() / res().y(),
                                              y_axis, center_qvec);
                        if (cur.isValid()) {
                            transformer->scale(cur.value() - center_qvec,{1, 0, 1}, scale_size);
                        }
                        interactingGraphics.insert(
                            zenovis::makeGraphicInteractingAxis(scene, center_zvec, {1, 0, 1}));
                    }
                    else if (m_pressedKeys.contains(Qt::Key_C)) {
                        // scale along z=center_qvec.z plane
                        auto cur = hitOnPlane((float)event->x() / res().x(), (float)event->y() / res().y(),
                                              z_axis, center_qvec);
                        if (cur.isValid()) {
                            transformer->scale(cur.value() - center_qvec,{1, 1, 0}, scale_size);
                        }
                        interactingGraphics.insert(
                            zenovis::makeGraphicInteractingAxis(scene, center_zvec, {1, 1, 0}));
                    }
                }
                else {
                    // scale along an axis
                    if (m_pressedKeys.contains(Qt::Key_Z)) {
                        // scale along x axis
                        auto cur = hitOnPlane((float)event->x() / res().x(), (float)event->y() / res().y(),
                                              y_axis, center_qvec);
                        if (cur.isValid()) {
                            transformer->scale(cur.value() - center_qvec,{1, 0, 0}, scale_size);
                        }
                        interactingGraphics.insert(
                            zenovis::makeGraphicInteractingAxis(scene, center_zvec, {1, 0, 0}));
                    }
                    else if (m_pressedKeys.contains(Qt::Key_X)) {
                        // scale along y=center_qvec.y plane
                        auto cur = hitOnPlane((float)event->x() / res().x(), (float)event->y() / res().y(),
                                              y_axis, center_qvec);
                        if (cur.isValid()) {
                            transformer->scale(cur.value() - center_qvec,{0, 1, 0}, scale_size);
                        }
                        interactingGraphics.insert(
                            zenovis::makeGraphicInteractingAxis(scene, center_zvec, {0, 1, 0}));
                    }
                    else if (m_pressedKeys.contains(Qt::Key_C)) {
                        auto cur = hitOnPlane((float)event->x() / res().x(), (float)event->y() / res().y(),
                                              z_axis, center_qvec);
                        if (cur.isValid()) {
                            transformer->scale(cur.value() - center_qvec,{0, 0, 1}, scale_size);
                        }
                        interactingGraphics.insert(
                            zenovis::makeGraphicInteractingAxis(scene, center_zvec, {0, 0, 1}));
                    }
                }
            }
            else if (ctrl_pressed){
                // translate
                if (m_pressedKeys.contains(Qt::Key_A)) {
                    // translate along a plane
                    if (m_pressedKeys.contains(Qt::Key_Z)) {
                        // translate along x=center_qvec.x plane
                        start = hitOnPlane((float)m_lastMovePos.x() / res().x(), (float)m_lastMovePos.y() / res().y(),
                                           x_axis, center_qvec);
                        end = hitOnPlane((float)event->x() / res().x(), (float)event->y() / res().y(),
                                         x_axis, center_qvec);
                        if (start.isValid() && end.isValid()) {
                            transformer->translate(start.value(),
                                                   end.value(), {0, 1, 1});
                        }
                        interactingGraphics.insert(
                            zenovis::makeGraphicInteractingAxis(scene, center_zvec,{0, 1, 1}));
                    }
                    else if (m_pressedKeys.contains(Qt::Key_X)) {
                        // translate along y=center_qvec.y plane
                        start = hitOnPlane((float)m_lastMovePos.x() / res().x(), (float)m_lastMovePos.y() / res().y(),
                                           y_axis, center_qvec);
                        end = hitOnPlane((float)event->x() / res().x(), (float)event->y() / res().y(),
                                         y_axis, center_qvec);
                        if (start.isValid() && end.isValid()) {
                            transformer->translate(start.value(),
                                                   end.value(), {1, 0, 1});
                        }
                        interactingGraphics.insert(
                            zenovis::makeGraphicInteractingAxis(scene, center_zvec, {1, 0, 1}));
                    }
                    else if (m_pressedKeys.contains(Qt::Key_C)) {
                        // translate along z=center_qvec.z plane
                        start = hitOnPlane((float)m_lastMovePos.x() / res().x(), (float)m_lastMovePos.y() / res().y(),
                                           z_axis, center_qvec);
                        end = hitOnPlane((float)event->x() / res().x(), (float)event->y() / res().y(),
                                         z_axis, center_qvec);
                        if (start.isValid() && end.isValid()) {
                            transformer->translate(start.value(),
                                                   end.value(), {1, 1, 0});
                        }
                        interactingGraphics.insert(
                            zenovis::makeGraphicInteractingAxis(scene, center_zvec, {1, 1, 0}));
                    }
                }
                else {
                    // translate along an axis
                    QVector3D translate_axis;
                    if (m_pressedKeys.contains(Qt::Key_Z)) {
                        // translate along x axis
                        start = hitOnPlane((float)m_lastMovePos.x() / res().x(), (float)m_lastMovePos.y() / res().y(),
                                           y_axis, center_qvec);
                        end = hitOnPlane((float)event->x() / res().x(), (float)event->y() / res().y(),
                                         y_axis, center_qvec);
                        if (start.isValid() && end.isValid()) {
                            transformer->translate(start.value(),end.value(), x_axis);
                        }
                        interactingGraphics.insert(
                            zenovis::makeGraphicInteractingAxis(scene, center_zvec, {1, 0, 0}));
                    }
                    else if (m_pressedKeys.contains(Qt::Key_X)) {
                        // translate along y=center_qvec.y plane
                        start = hitOnPlane((float)m_lastMovePos.x() / res().x(), (float)m_lastMovePos.y() / res().y(),
                                           z_axis, center_qvec);
                        end = hitOnPlane((float)event->x() / res().x(), (float)event->y() / res().y(),
                                         z_axis, center_qvec);
                        if (start.isValid() && end.isValid()) {
                            transformer->translate(start.value(),end.value(), y_axis);
                        }
                        interactingGraphics.insert(
                            zenovis::makeGraphicInteractingAxis(scene, center_zvec, {0, 1, 0}));
                    }
                    else if (m_pressedKeys.contains(Qt::Key_C)) {
                        // translate along z=center_qvec.z plane
                        start = hitOnPlane((float)m_lastMovePos.x() / res().x(), (float)m_lastMovePos.y() / res().y(),
                                           y_axis, center_qvec);
                        end = hitOnPlane((float)event->x() / res().x(), (float)event->y() / res().y(),
                                         y_axis, center_qvec);
                        if (start.isValid() && end.isValid()) {
                            transformer->translate(start.value(),end.value(), z_axis);
                        }
                        interactingGraphics.insert(
                            zenovis::makeGraphicInteractingAxis(scene, center_zvec, {0, 0, 1}));
                    }
                }
            }
            else {
                // free translate (along sight plane)
                QVector3D camera_front(scene->camera->m_lodfront[0],
                                       scene->camera->m_lodfront[1],
                                       scene->camera->m_lodfront[2]);
                start = hitOnPlane((float)m_lastMovePos.x() / res().x(), (float)m_lastMovePos.y() / res().y(),
                                   camera_front, center_qvec);
                end = hitOnPlane((float)event->x() / res().x(), (float)event->y() / res().y(),
                                 camera_front, center_qvec);
                if (start.isValid() && end.isValid()) {
                    transformer->translate(start.value(),end.value(), {1, 1, 1});
                }
            }
            session->set_interacting_graphics(interactingGraphics);
            m_lastMovePos = event->pos();
            zenoApp->getMainWindow()->updateViewport();
        }
        else {
            float min_x = std::min((float)m_boundRectStartPos.x(), (float)event->x()) / m_res.x();
            float max_x = std::max((float)m_boundRectStartPos.x(), (float)event->x()) / m_res.x();
            float min_y = std::min((float)m_boundRectStartPos.y(), (float)event->y()) / m_res.y();
            float max_y = std::max((float)m_boundRectStartPos.y(), (float)event->y()) / m_res.y();
            scene->select_box = zeno::vec4f(min_x, min_y, max_x, max_y);
        }
    }
    else {
        if (mouseEnteredRing(event->x(), event->y())) {
            Zenovis::GetInstance().getSession()->set_hovered_graphic("ring");
        }
        else {
            Zenovis::GetInstance().getSession()->set_hovered_graphic("");
        }
    }
    updatePerspective();
}

void CameraControl::updatePerspective()
{
    float cx = m_center[0], cy = m_center[1], cz = m_center[2];
    Zenovis::GetInstance().updatePerspective(m_res, PerspectiveInfo(cx, cy, cz, m_theta, m_phi, m_radius, m_fov, m_ortho_mode, m_aperture, m_focalPlaneDistance));
}

void CameraControl::fakeWheelEvent(QWheelEvent* event)
{
    int dy = event->angleDelta().y();
    float scale = (dy >= 0) ? 0.89 : 1 / 0.89;
    bool shift_pressed = (event->modifiers() & Qt::ShiftModifier) && !(event->modifiers() & Qt::ControlModifier);
    bool aperture_pressed = (event->modifiers() & Qt::ControlModifier) && !(event->modifiers() & Qt::ShiftModifier);
    bool focalPlaneDistance_pressed = (event->modifiers() & Qt::ControlModifier) && (event->modifiers() & Qt::ShiftModifier);
    float delta = dy > 0? 1: -1;
    if (shift_pressed){
        float temp = m_fov / scale;
        m_fov = temp < 170 ? temp : 170;

    }
    else if (aperture_pressed) {
        float temp = m_aperture += delta * 0.01;
        m_aperture = temp >= 0 ? temp : 0;  

    }
    else if (focalPlaneDistance_pressed) {
        float temp = m_focalPlaneDistance + delta*0.05;
        m_focalPlaneDistance = temp >= 0.05 ? temp : 0.05;
    }
    else {
        m_radius *= scale;
    }
    updatePerspective();

    if(zenoApp->getMainWindow()->lightPanel != nullptr){
        zenoApp->getMainWindow()->lightPanel->camApertureEdit->setText(QString::number(m_aperture));
        zenoApp->getMainWindow()->lightPanel->camDisPlaneEdit->setText(QString::number(m_focalPlaneDistance));
    }
}

void CameraControl::setKeyFrame()
{
    //todo
}

void CameraControl::focus(QVector3D center, float radius)
{
    m_center = center;
    if (m_fov >= 1e-6)
        radius /= (m_fov / 45.0f);
    m_radius = radius;
    updatePerspective();
}

QVector3D CameraControl::realPos() const {
    float cos_t = std::cos(m_theta);
    float sin_t = std::sin(m_theta);
    float cos_p = std::cos(m_phi);
    float sin_p = std::sin(m_phi);
    QVector3D back(cos_t * sin_p, sin_t, -cos_t * cos_p);
    return m_center - back * m_radius;
}

// x, y from [0, 1]
QVector3D CameraControl::screenToWorldRay(float x, float y) const {
    float cos_t = cos(m_theta);
    float sin_t = sin(m_theta);
    float cos_p = cos(m_phi);
    float sin_p = sin(m_phi);
    QVector3D back(cos_t * sin_p, sin_t, -cos_t * cos_p);
    QVector3D up(-sin_t * sin_p, cos_t, sin_t * cos_p);
    QVector3D right = QVector3D::crossProduct(up, back);
    up = QVector3D::crossProduct(back, right);
    right.normalize();
    up.normalize();
    QMatrix4x4 view;
    view.setToIdentity();
    view.lookAt(realPos(), m_center, up);
    x = (x - 0.5) * 2;
    y = (y - 0.5) * (-2);
    float v = std::tan(m_fov * M_PI / 180.f * 0.5f);
    float aspect = res().x() / res().y();
    auto dir = QVector3D(v * x * aspect, v * y, -1);
    dir = dir.normalized();
    dir = view.inverted().mapVector(dir);
    return dir;
}

QVariant CameraControl::hitOnFloor(float x, float y) const {
    auto dir = screenToWorldRay(x, y);
    auto pos = realPos();
    float t = (0 - pos.y()) / dir.y();
    if (t > 0) {
        auto p = pos + dir * t;
        return p;
    } else {
        return {};
    }
}

QVariant CameraControl::hitOnPlane(float x, float y, QVector3D n, QVector3D p) const {
    auto dir = screenToWorldRay(x, y);
    auto pos = realPos();
    float t = (n.x()*p.x() + n.y()*p.y() + n.z()*p.z() - n.x()*pos.x() - n.y()*pos.y() - n.z()*pos.z()) /
              (n.x()*dir.x() + n.y()*dir.y() + n.z()*dir.z());
    if (t > 0)
        return pos + dir * t;
    else
        return {};
}

void CameraControl::fakeMouseReleaseEvent(QMouseEvent *event) {
    if (event->button() == Qt::LeftButton) {

        //if (Zenovis::GetInstance().m_bAddPoint == true) {
        //float x = (float)event->x() / m_res.x();
        //float y = (float)event->y() / m_res.y();
        //auto rdir = screenToWorldRay(x, y);
        //auto pos = realPos();
        //float t = (0 - pos.y()) / rdir.y();
        //auto p = pos + rdir * t;

        //float cos_t = std::cos(m_theta);
        //float sin_t = std::sin(m_theta);
        //float cos_p = std::cos(m_phi);
        //float sin_p = std::sin(m_phi);
        //QVector3D back(cos_t * sin_p, sin_t, -cos_t * cos_p);
        //QVector3D up(-sin_t * sin_p, cos_t, sin_t * cos_p);
        //QVector3D right = QVector3D::crossProduct(up, back).normalized();
        //up = QVector3D::crossProduct(right, back).normalized();
        //QVector3D delta = right * x + up * y;

        //zeno::log_info("create point at x={} y={}", p[0], p[1]);

        createPointNode(QPointF(p[0], p[1]));

        //Zenovis::GetInstance().m_bAddPoint = false;
        //}

        auto scene = Zenovis::GetInstance().getSession()->get_scene();

        if (transformer->isTransforming()) {
            bool moved = false;
            if (m_boundRectStartPos != event->pos()) {
                // create/modify transform primitive node
                moved = true;
            }
            transformer->endTransform(moved);
        }
        else {
            auto cam_pos = realPos();

            scene->select_box = std::nullopt;
            bool shift_pressed = event->modifiers() & Qt::ShiftModifier;
            if (!shift_pressed) {
                scene->selected.clear();
                transformer->clear();
            }

            QPoint releasePos = event->pos();
            if (m_boundRectStartPos == releasePos) {
                float x = (float)event->x() / m_res.x();
                float y = (float)event->y() / m_res.y();
                auto rdir = screenToWorldRay(x, y);
                float min_t = std::numeric_limits::max();
                std::string name("");
                for (auto const &[key, ptr] : scene->objectsMan->pairs()) {
                    zeno::vec3f center;
                    float radius;
                    zeno::vec3f ro(cam_pos[0], cam_pos[1], cam_pos[2]);
                    zeno::vec3f rd(rdir[0], rdir[1], rdir[2]);
//                    if (zeno::objectGetFocusCenterRadius(ptr, center, radius)) {
//                        if (auto ret = ray_sphere_intersect(ro, rd, center, radius)) {
//                            float t = *ret;
//                            if (t < min_t) {
//                                min_t = t;
//                                name = key;
//                            }
//                        }
//                    }

                    zeno::vec3f bmin,bmax;
                    if (zeno::objectGetBoundingBox(ptr,bmin,bmax) ){
                        if (auto ret = ray_box_intersect(bmin, bmax, ro, rd)) {
                            float t = *ret;
                            if (t < min_t) {
                                min_t = t;
                                name = key;
                            }
                        }
                    }
                }
                if (!name.empty()) {
                    if (scene->selected.count(name) > 0) {
                        scene->selected.erase(name);
                        transformer->removeObject(name);
                    } else {
                        scene->selected.insert(name);
                        transformer->addObject(name);
                        // printf("selected %s\n", name.c_str());
                    }
                }
            } else {
                float min_x = std::min((float)m_boundRectStartPos.x(), (float)releasePos.x());
                float max_x = std::max((float)m_boundRectStartPos.x(), (float)releasePos.x());
                float min_y = std::min((float)m_boundRectStartPos.y(), (float)releasePos.y());
                float max_y = std::max((float)m_boundRectStartPos.y(), (float)releasePos.y());
                auto left_up = screenToWorldRay(min_x / m_res.x(), min_y / m_res.y());
                auto left_down = screenToWorldRay(min_x / m_res.x(), max_y / m_res.y());
                auto right_up = screenToWorldRay(max_x / m_res.x(), min_y / m_res.y());
                auto right_down = screenToWorldRay(max_x / m_res.x(), max_y / m_res.y());
                auto cam_posWS = realPos();
                auto left_normWS = QVector3D::crossProduct(left_down, left_up);
                auto right_normWS = QVector3D::crossProduct(right_up, right_down);
                auto up_normWS = QVector3D::crossProduct(left_up, right_up);
                auto down_normWS = QVector3D::crossProduct(right_down, left_down);

                std::vector passed_prim;
                for (auto const &[key, ptr] : scene->objectsMan->pairs()) {
                    zeno::vec3f c;
                    float radius;
                    if (zeno::objectGetFocusCenterRadius(ptr, c, radius)) {
                        bool passed = test_in_selected_bounding(QVector3D(c[0], c[1], c[2]), cam_pos, left_normWS,
                                                                right_normWS, up_normWS, down_normWS);
                        if (passed) {
                            passed_prim.push_back(key);
                            transformer->addObject(key);
                        }
                    }
                }
                scene->selected.insert(passed_prim.begin(), passed_prim.end());

            }
        }

        if (!scene->selected.empty()) {
            auto center = transformer->getCenter();
            zeno::vec3f ring_center(center[0], center[1], center[2]);
            std::map> interactGraphics;
            interactGraphics["ring"] = zenovis::makeGraphicRing(scene, ring_center);
            interactGraphics["axis"] = zenovis::makeGraphicInteractAxis(scene, ring_center);
            Zenovis::GetInstance().getSession()->set_interactive_graphics(interactGraphics);

            zenoApp->getMainWindow()->updateViewport();
        }

        ZenoMainWindow* mainWin = zenoApp->getMainWindow();
        mainWin->onPrimitiveSelected(scene->selected);
    }
}

void CameraControl::addPressedKey(int key) {
    m_pressedKeys += key;
}

void CameraControl::rmvPressedKey(int key) {
    m_pressedKeys -= key;
}

//void CameraControl::createPointNode(QPointF pnt) {
    //auto pModel = zenoApp->graphsManagment()->currentModel();
	//ZASSERT_EXIT(pModel);
    todo luzh: select specific subgraph to add node.
    //const QModelIndex &subgIdx = pModel->index("main");
    //NODE_DATA tmpNodeInfo = NodesMgr::createPointNode(pModel, subgIdx, "CreatePoint", {10, 10}, pnt);        

    //STATUS_UPDATE_INFO info;
    //info.role = ROLE_OPTIONS;
    //info.newValue = OPT_VIEW;
    //pModel->updateNodeStatus(tmpNodeInfo[ROLE_OBJID].toString(), info, subgIdx, true);
//}

ViewportWidget::ViewportWidget(QWidget* parent)
    : QOpenGLWidget(parent)
    , m_camera(nullptr)
    , updateLightOnce(true)
{
    QSurfaceFormat fmt;
    int nsamples = 16;  // TODO: adjust in a zhouhang-panel
    fmt.setSamples(nsamples);
    fmt.setVersion(3, 2);
    fmt.setProfile(QSurfaceFormat::CoreProfile);
    setFormat(fmt);

    m_camera = std::make_shared();
    Zenovis::GetInstance().m_camera_control = m_camera.get();
}

ViewportWidget::~ViewportWidget()
{
}

namespace {
struct OpenGLProcAddressHelper {
    inline static QOpenGLContext *ctx;

    static void *getProcAddress(const char *name) {
        return (void *)ctx->getProcAddress(name);
    }
};
}

void ViewportWidget::initializeGL()
{
    OpenGLProcAddressHelper::ctx = context();
    Zenovis::GetInstance().loadGLAPI((void *)OpenGLProcAddressHelper::getProcAddress);
    Zenovis::GetInstance().initializeGL();
}

void ViewportWidget::resizeGL(int nx, int ny)
{
    float ratio = devicePixelRatioF();
    zeno::log_trace("nx={}, ny={}, dpr={}", nx, ny, ratio);
    m_camera->setRes(QVector2D(nx * ratio, ny * ratio));
    m_camera->updatePerspective();
}

QVector2D ViewportWidget::cameraRes() const
{
    return m_camera->res();
}

void ViewportWidget::setCameraRes(const QVector2D& res)
{
    m_camera->setRes(res);
}

void ViewportWidget::updatePerspective()
{
    m_camera->updatePerspective();
}

void ViewportWidget::paintGL()
{
    Zenovis::GetInstance().paintGL();
    if(updateLightOnce){
        auto scene = Zenovis::GetInstance().getSession()->get_scene();

        if(scene->objectsMan->lightObjects.size() > 0){
            zenoApp->getMainWindow()->updateLightList();
            updateLightOnce = false;
        }
    }

    if (!record_path.empty() /*&& f <= frame_end*/) //py has bug: frame_end not initialized.
    {
        int f = Zenovis::GetInstance().getCurrentFrameId();
        auto record_file = zeno::format("{}/{:06d}.png", record_path, f);
        checkRecord(record_file, record_res);
    }
}

void ViewportWidget::checkRecord(std::string a_record_file, QVector2D a_record_res)
{
    if (!record_path.empty() /*&& f <= frame_end*/) //py has bug: frame_end not initialized.
    {
        QVector2D oldRes = m_camera->res();
        m_camera->setRes(a_record_res);
        m_camera->updatePerspective();
        auto extname = QFileInfo(QString::fromStdString(a_record_file)).suffix().toStdString();
        Zenovis::GetInstance().getSession()->do_screenshot(a_record_file, extname);
        m_camera->setRes(oldRes);
        m_camera->updatePerspective();
        //if f == self.frame_end:
        //    self.parent_widget.record_video.finish_record()
    }
}

void ViewportWidget::mousePressEvent(QMouseEvent* event)
{
    _base::mousePressEvent(event);
    m_camera->fakeMousePressEvent(event);
    update();
}

void ViewportWidget::mouseMoveEvent(QMouseEvent* event)
{
    _base::mouseMoveEvent(event);
    m_camera->fakeMouseMoveEvent(event);
    update();
}

void ViewportWidget::wheelEvent(QWheelEvent* event)
{
    _base::wheelEvent(event);
    m_camera->fakeWheelEvent(event);
    update();
}

void ViewportWidget::mouseReleaseEvent(QMouseEvent *event) {    
    _base::mouseReleaseEvent(event);
    m_camera->fakeMouseReleaseEvent(event); 
    update();
}

void ViewportWidget::addPressedKey(int key) {
    m_camera->addPressedKey(key);
}

void ViewportWidget::rmvPressedKey(int key) {
    m_camera->rmvPressedKey(key);
}

void ViewportWidget::updateCameraProp(float aperture, float disPlane) {
    m_camera->setAperture(aperture);
    m_camera->setDisPlane(disPlane);
    updatePerspective();
}

/*
QDMDisplayMenu::QDMDisplayMenu()r("Show Grid")
{
    setTitle(tr("Display"));
    QAction* pAction = new QAction(tr("Show Grid"), this);
    pAction->setCheckable(true);
    pAction->setChecked(true);
    addAction(pAction);

    pAction = new QAction(tr("Background Color"), this);
    addAction(pAction);

    addSeparator();

    pAction = new QAction(tr("Smooth Shading"), this);
    pAction->setCheckable(true);
    pAction->setChecked(false);
    addAction(pAction);

    pAction = new QAction(tr("Wireframe"), this);
    pAction->setCheckable(true);
    pAction->setChecked(false);

    addSeparator();

    pAction = new QAction(tr("Enable PBR"), this);
    pAction->setCheckable(true);
    pAction->setChecked(false);
    addAction(pAction);

    pAction = new QAction(tr("Enable GI"), this);
    pAction->setCheckable(true);
    pAction->setChecked(false);
    addAction(pAction);

    addSeparator();

    pAction = new QAction(tr("Camera Keyframe"), this);
    addAction(pAction);

    addSeparator();

    pAction = new QAction(tr("English / Chinese"), this);
    pAction->setCheckable(true);
    pAction->setChecked(true);
    addAction(pAction);
}

QDMRecordMenu::QDMRecordMenu()
{
    setTitle(tr("Record"));

    QAction* pAction = new QAction(tr("Screenshot"), this);
    pAction->setShortcut(QKeySequence("F12"));
    addAction(pAction);

    pAction = new QAction(tr("Record Video"), this);
    pAction->setShortcut(QKeySequence(tr("Shift+F12")));
    addAction(pAction);
}
*/


DisplayWidget::DisplayWidget(ZenoMainWindow* pMainWin)
    : QWidget(pMainWin)
    , m_view(nullptr)
    , m_timeline(nullptr)
    , m_mainWin(pMainWin)
    , m_pTimer(nullptr)
{
    QVBoxLayout* pLayout = new QVBoxLayout;
    pLayout->setContentsMargins(0, 0, 0, 0);
    pLayout->setSpacing(0);

    setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);

    /*
    ZMenuBar* menuBar = new ZMenuBar;
    menuBar->setMaximumHeight(26);

    QDMDisplayMenu* menuDisplay = new QDMDisplayMenu;
    menuBar->addMenu(menuDisplay);
    QDMRecordMenu* recordDisplay = new QDMRecordMenu;
    menuBar->addMenu(recordDisplay);

    pLayout->addWidget(menuBar);
    */

    m_view = new ViewportWidget;
    // viewport interaction need to set mouse tracking true
    // but it will lead to a light panel edit bug
    // m_view->setMouseTracking(true);
    pLayout->addWidget(m_view);

    m_timeline = new ZTimeline;
    pLayout->addWidget(m_timeline);
    setLayout(pLayout);

    //RecordVideoDialog
    m_camera_keyframe = new CameraKeyframeWidget;
    Zenovis::GetInstance().m_camera_keyframe = m_camera_keyframe;

	connect(&Zenovis::GetInstance(), SIGNAL(frameUpdated(int)), m_timeline, SLOT(onTimelineUpdate(int)));
    connect(m_timeline, SIGNAL(playForward(bool)), this, SLOT(onPlayClicked(bool)));
	connect(m_timeline, SIGNAL(sliderValueChanged(int)), this, SLOT(onSliderValueChanged(int)));
	connect(m_timeline, SIGNAL(run()), this, SLOT(onRun()));
    connect(m_timeline, SIGNAL(alwaysChecked()), this, SLOT(onRun()));
    connect(m_timeline, SIGNAL(kill()), this, SLOT(onKill()));

    //connect(m_view, SIGNAL(sig_Draw()), this, SLOT(onRun()));

    auto graphs = zenoApp->graphsManagment();
    connect(&*graphs, SIGNAL(modelDataChanged()), this, SLOT(onModelDataChanged()));

	m_pTimer = new QTimer(this);
    connect(m_pTimer, SIGNAL(timeout()), this, SLOT(updateFrame()));
}

DisplayWidget::~DisplayWidget()
{

}

void DisplayWidget::init()
{
    //m_camera->installEventFilter(this);
}

TIMELINE_INFO DisplayWidget::timelineInfo()
{
    TIMELINE_INFO info;
    info.bAlways = m_timeline->isAlways();
    info.beginFrame = m_timeline->fromTo().first;
    info.endFrame = m_timeline->fromTo().second;
    return info;
}

void DisplayWidget::setTimelineInfo(TIMELINE_INFO info)
{
    m_timeline->setAlways(info.bAlways);
    m_timeline->setFromTo(info.beginFrame, info.endFrame);
}

ViewportWidget* DisplayWidget::getViewportWidget()
{
    return m_view;
}

QSize DisplayWidget::sizeHint() const
{
    return ZenoStyle::dpiScaledSize(QSize(12, 400));
}

void DisplayWidget::updateFrame(const QString &action) // cihou optix
{
    if (m_mainWin && m_mainWin->inDlgEventLoop())
        return;

    if (action == "newFrame") {
        m_pTimer->stop();
        //zeno::log_warn("stop");
        return;
    } else if (action == "finishFrame") {
        if (isOptxRendering()) {
            m_pTimer->start(m_updateFeq);
        }
    } else if (!action.isEmpty()) {
        if (action == "optx") {
            m_pTimer->start(m_updateFeq);
        } else {
            m_pTimer->stop();
        }
    }
    m_view->update();
}

void DisplayWidget::onFinished()
{
    int frameid_ui = m_timeline->value();
    if (frameid_ui != Zenovis::GetInstance().getCurrentFrameId())
    {
        Zenovis::GetInstance().setCurrentFrameId(frameid_ui);
        updateFrame();
        onPlayClicked(false);
        BlockSignalScope scope(m_timeline);
        m_timeline->setPlayButtonToggle(false);
    }
}

bool DisplayWidget::isOptxRendering() const
{
    auto& inst = Zenovis::GetInstance();
    auto sess = inst.getSession();
    if (!sess)
        return false;
    auto scene = sess->get_scene();
    if (!scene)
        return false;

    return (scene->renderMan && scene->renderMan->getDefaultEngineName() == "optx");
}

void DisplayWidget::onModelDataChanged()
{
    if (m_timeline->isAlways())
    {
        onRun();
    }
}

void DisplayWidget::onPlayClicked(bool bChecked)
{
    if (bChecked)
    {
        m_pTimer->start(m_sliderFeq);
    }
    else
    {
        if (!isOptxRendering())
            m_pTimer->stop();
    }
    Zenovis::GetInstance().startPlay(bChecked);
}

void DisplayWidget::onSliderValueChanged(int frame)
{
    m_mainWin->clearErrorMark();

    if (m_timeline->isAlways())
    {
        auto pGraphsMgr = zenoApp->graphsManagment();
        IGraphsModel* pModel = pGraphsMgr->currentModel();
        if (!pModel)
            return;
        launchProgram(pModel, frame, frame);
    }
    else
    {
        Zenovis::GetInstance().setCurrentFrameId(frame);
        updateFrame();
        onPlayClicked(false);
        BlockSignalScope scope(m_timeline);
        m_timeline->setPlayButtonToggle(false);
    }
}

void DisplayWidget::onRun()
{
    m_mainWin->clearErrorMark();

    Zenovis::GetInstance().getSession()->get_scene()->selected.clear();

    QPair fromTo = m_timeline->fromTo();
    int beginFrame = fromTo.first;
    int endFrame = fromTo.second;
    if (endFrame >= beginFrame && beginFrame >= 0)
    {
        auto pGraphsMgr = zenoApp->graphsManagment();
        IGraphsModel* pModel = pGraphsMgr->currentModel();
        if (!pModel)
            return;
        launchProgram(pModel, beginFrame, endFrame);
    }
    else
    {

    }

    m_view->updateLightOnce = true;
    auto scene = Zenovis::GetInstance().getSession()->get_scene();
    scene->objectsMan->lightObjects.clear();
}

void DisplayWidget::keyPressEvent(QKeyEvent* event) {
    if (!event->isAutoRepeat())
        m_view->addPressedKey(event->key());
}

void DisplayWidget::keyReleaseEvent(QKeyEvent* event) {
    if (!event->isAutoRepeat())
        m_view->rmvPressedKey(event->key());
}


void DisplayWidget::onRecord()
{
    auto& inst = Zenovis::GetInstance();

    auto& ptr = zeno::getSession().globalComm;
    ZASSERT_EXIT(ptr);

    if (ptr->maxPlayFrames() == 0) {
        //run.
        QMessageBox::information(nullptr, "Zeno", tr("Run the graph before recording"), QMessageBox::Ok);
        return;
    }

    int frameLeft = ptr->beginFrameNumber;
    int frameRight = ptr->endFrameNumber;

    ZRecordVideoDlg dlg(frameLeft, frameRight, this);
    if (QDialog::Accepted == dlg.exec())
    {
        int frameStart = 0, frameEnd = 0, fps = 0, bitrate = 0, width = 0, height = 0;
        QString presets, path, filename;
        dlg.getInfo(frameStart, frameEnd, fps, bitrate, presets, width, height, path, filename);
        //validation.

        VideoRecInfo recInfo;
        recInfo.record_path = path;
        recInfo.frameRange = { frameStart, frameEnd };
        recInfo.res = { (float)width, (float)height };
        recInfo.bitrate = bitrate;
        recInfo.fps = fps;
        recInfo.videoname = filename;

        Zenovis::GetInstance().startPlay(true);

        RecordVideoMgr recordMgr(m_view, recInfo, nullptr);
        ZRecordProgressDlg dlgProc(recInfo);
        connect(&recordMgr, SIGNAL(frameFinished(int)), &dlgProc, SLOT(onFrameFinished(int)));
        connect(&recordMgr, SIGNAL(recordFinished()), &dlgProc, SLOT(onRecordFinished()));
        connect(&recordMgr, SIGNAL(recordFailed(QString)), &dlgProc, SLOT(onRecordFailed(QString)));
        if (QDialog::Accepted == dlgProc.exec())
        {
        }
        else
        {
            recordMgr.cancelRecord();
        }
    }
}

void DisplayWidget::onKill()
{
    killProgram();
}

 

 

你可能感兴趣的:(ZENO,开发日志,学习,ZENO)