longlene / cl-raylib

Common Lisp binding of raylib
MIT License
145 stars 21 forks source link

Information on camera update #15

Closed giuliods closed 2 years ago

giuliods commented 2 years ago

With setting the camera3d's mode to free I have to update it on every frame. But the function require the camera to be passed as a pointer. I actually have +camera+ storing the camera3d struct. There is a function or a macro I'm not aware to pass it to the update-camera function?

longlene commented 2 years ago

@giuliods Please have a look at https://github.com/longlene/cl-raylib/issues/12 That's just the question we talked.

longlene commented 2 years ago

@giuliods Currently, seems no solution for it.

kiran-kp commented 2 years ago

Here's my attempt at some macros to help solve this.

This is my first attempt at doing anything serious with macros so I'm very open to feedback.

(defmacro define-struct (name description fields)
  (let* ((cstruct-name (read-from-string (concatenate 'string "%" (string-trim " " name))))
         (cstruct-type (read-from-string (concatenate 'string "%" (string-trim " " name) "-tclass")))
         (lisp-struct-name (read-from-string name))
         (field-names (loop :for f :in fields :collect (nth 0 f))))
    `(progn
       (cffi:defcstruct ,cstruct-name
         ,description
         ,@fields)
       (defstruct ,lisp-struct-name
         ,description
         ,@(loop :for f :in fields
                 :collect (nth 0 f)))
       (defmethod cffi:translate-into-foreign-memory (lisp-var (type ,cstruct-type) ptr)
         (cffi:with-foreign-slots (,field-names ptr (:struct cstruct-name))
           ,@(loop :for f :in field-names
                   :collect `(setf ,f (,(read-from-string (concatenate 'string name "-" (string f))) lisp-var)))))
       (defmethod cffi:translate-from-foreign (ptr (type ,cstruct-type))
         (cffi:with-foreign-slots (,field-names ptr (:struct ,cstruct-name))
           (,(read-from-string (concatenate 'string "make-" name))
            ,@(loop :for f :in field-names
                    :collect `(,(read-from-string (concatenate 'string ":" (string f))) ,f) :into s
                    :finally (return (apply #'append s))))))
       (defmacro ,(read-from-string (concatenate 'string "update-" name "-from-foreign"))
           (lisp-var ptr)
         (,(read-from-string "sb-int:quasiquote")
          (progn
            ,@(loop :for f :in field-names
                    :collect `(setf (,(read-from-string (concatenate 'string name "-" (string f)))
                                     (unquote lisp-var))
                                    (cffi:foreign-slot-value ptr '(:struct ,cstruct-name) ',f)))))))))

;; Example
(define-struct "Camera3D"
  "Camera, defines position/orientation in 3d space"
  ((position (:struct %Vector3))
   (target (:struct %Vector3))
   (up (:struct %Vector3))
   (fovy :float)
   (projection :int)))

(cffi:defcfun ("UpdateCamera" %update-camera) :void
  "Update camera position for selected mode"
  (camera (:pointer (:struct %Camera3D))))

(defmacro update-camera (camera)
  (let ((foreign-camera (gensym)))
    `(cffi:with-foreign-object (,foreign-camera (:struct %Camera))
       (cffi:translate-into-foreign-memory ,camera %Camera-tclass ,foreign-camera)
       (%update-camera ,foreign-camera)
       (update-camera3d-from-foreign ,camera ,foreign-camera))))

;; Example
(update-camera test)

Here's an example of what the macros expand to:

;; (define-struct "Camera3D"...)
(progn
 (cffi:defcstruct %camera3d
   "Camera, defines position/orientation in 3d space"
   (position (:struct %vector3))
   (target (:struct %vector3))
   (up (:struct %vector3))
   (fovy :float)
   (projection :int))
 (defstruct camera3d
   "Camera, defines position/orientation in 3d space"
   position
   target
   up
   fovy
   projection)
 (defmethod cffi:translate-into-foreign-memory
            (lisp-var (type %camera3d-tclass) ptr)
   (cffi:with-foreign-slots ((position target up fovy projection) ptr
                             (:struct cstruct-name))
     (setf position (camera3d-position lisp-var))
     (setf target (camera3d-target lisp-var))
     (setf up (camera3d-up lisp-var))
     (setf fovy (camera3d-fovy lisp-var))
     (setf projection (camera3d-projection lisp-var))))
 (defmethod cffi:translate-from-foreign (ptr (type %camera3d-tclass))
   (cffi:with-foreign-slots ((position target up fovy projection) ptr
                             (:struct %camera3d))
     (make-camera3d :position position :target target :up up :fovy fovy
                    :projection projection)))
 (defmacro update-camera3d-from-foreign (lisp-var ptr)
   `(progn
     (setf (camera3d-position (unquote lisp-var))
             (cffi:foreign-slot-value ptr '(:struct %camera3d) 'position))
     (setf (camera3d-target (unquote lisp-var))
             (cffi:foreign-slot-value ptr '(:struct %camera3d) 'target))
     (setf (camera3d-up (unquote lisp-var))
             (cffi:foreign-slot-value ptr '(:struct %camera3d) 'up))
     (setf (camera3d-fovy (unquote lisp-var))
             (cffi:foreign-slot-value ptr '(:struct %camera3d) 'fovy))
     (setf (camera3d-projection (unquote lisp-var))
             (cffi:foreign-slot-value ptr '(:struct %camera3d) 'projection)))))

;; (update-camera test)
(cffi:with-foreign-object (#:g636 (:struct %camera))
  (cffi:translate-into-foreign-memory test %camera-tclass #:g636)
  (%update-camera #:g636)
  (update-camera3d-from-foreign test #:g636))

It should be fairly simple to create a macro that generates the update-camera type functions too.

kiran-kp commented 2 years ago

Oh, I should add that the define-struct isn't portable because I can't figure out nested backquotes. I wrote this in sbcl but I'll keep working on cleaning it up.