├── data-raw ├── berries.png └── doomhud.png ├── .Rbuildignore ├── src ├── Makevars ├── vector.h ├── event_receiver.h ├── rect.h ├── RcppExports.cpp ├── quaternion.h ├── geom_vector_calc.h └── plot_irr.cpp ├── R ├── sys_functions.R ├── mesh2ply_string.R └── RcppExports.R ├── NAMESPACE ├── .gitignore ├── man ├── mesh2ply_string.Rd └── plot_irr.Rd ├── Rirrlicht.Rproj ├── playground ├── meshes.R ├── stackoverflow_example.R └── rasters.R ├── .travis.yml ├── DESCRIPTION └── README.md /data-raw/berries.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nevrome/Rirrlicht/master/data-raw/berries.png -------------------------------------------------------------------------------- /data-raw/doomhud.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nevrome/Rirrlicht/master/data-raw/doomhud.png -------------------------------------------------------------------------------- /.Rbuildignore: -------------------------------------------------------------------------------- 1 | ^.*\.Rproj$ 2 | ^\.Rproj\.user$ 3 | data-raw 4 | ^\.travis\.yml$ 5 | ^playground$ 6 | -------------------------------------------------------------------------------- /src/Makevars: -------------------------------------------------------------------------------- 1 | PKG_LIBS = -lIrrlicht 2 | PKG_CPPFLAGS = -I /usr/include/irrlicht/ 3 | CXX_STD = CXX11 -------------------------------------------------------------------------------- /R/sys_functions.R: -------------------------------------------------------------------------------- 1 | # General roxygen tags 2 | #' @useDynLib Rirrlicht 3 | #' @importFrom Rcpp sourceCpp 4 | 5 | #' @export 6 | .onUnload <- function (libpath) { 7 | library.dynam.unload("Rirrlicht", libpath) 8 | } -------------------------------------------------------------------------------- /NAMESPACE: -------------------------------------------------------------------------------- 1 | # Generated by roxygen2: do not edit by hand 2 | 3 | export(.onUnload) 4 | export(mesh2ply_string) 5 | export(plot_irr) 6 | importFrom(Rcpp,sourceCpp) 7 | importFrom(magrittr,"%>%") 8 | useDynLib(Rirrlicht) 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .Rproj.user 2 | .Rhistory 3 | .RData 4 | .Ruserdata 5 | 6 | # bytecode 7 | src/*.o 8 | src/*.so 9 | src/*.dll 10 | 11 | # produced vignettes 12 | vignettes/*.html 13 | vignettes/*.pdf 14 | vignettes/*.R 15 | inst/doc/ -------------------------------------------------------------------------------- /man/mesh2ply_string.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/mesh2ply_string.R 3 | \name{mesh2ply_string} 4 | \alias{mesh2ply_string} 5 | \title{mesh2ply string version} 6 | \usage{ 7 | mesh2ply_string(mesh) 8 | } 9 | \arguments{ 10 | \item{mesh}{truet} 11 | } 12 | \value{ 13 | string that describes a mesh in ply format 14 | } 15 | \description{ 16 | Mask for Morpho::mesh2ply 17 | } 18 | -------------------------------------------------------------------------------- /Rirrlicht.Rproj: -------------------------------------------------------------------------------- 1 | Version: 1.0 2 | 3 | RestoreWorkspace: Default 4 | SaveWorkspace: Default 5 | AlwaysSaveHistory: Default 6 | 7 | EnableCodeIndexing: Yes 8 | UseSpacesForTab: Yes 9 | NumSpacesForTab: 2 10 | Encoding: UTF-8 11 | 12 | RnwWeave: Sweave 13 | LaTeX: pdfLaTeX 14 | 15 | BuildType: Package 16 | PackageUseDevtools: Yes 17 | PackageInstallArgs: --no-multiarch --with-keep.source 18 | PackageRoxygenize: rd,collate,namespace,vignette 19 | -------------------------------------------------------------------------------- /R/mesh2ply_string.R: -------------------------------------------------------------------------------- 1 | #' mesh2ply string version 2 | #' 3 | #' Mask for Morpho::mesh2ply 4 | #' 5 | #' @param mesh truet 6 | #' 7 | #' @return string that describes a mesh in ply format 8 | #' 9 | #' @importFrom magrittr "%>%" 10 | #' 11 | #' @export 12 | mesh2ply_string <- function(mesh) { 13 | rawConnection(raw(0), "r+") -> zz 14 | Morpho::mesh2ply(x = mesh, filename = zz) 15 | rawConnectionValue(zz) %>% 16 | rawToChar() -> res 17 | close.connection(zz) 18 | return(res) 19 | } 20 | -------------------------------------------------------------------------------- /playground/meshes.R: -------------------------------------------------------------------------------- 1 | library(rgl) 2 | library(magrittr) 3 | 4 | rgl::cuboctahedron3d() %>% 5 | mesh2ply_string() %>% 6 | c(.) %>% 7 | plot_irr(mesh_cv = .) 8 | 9 | hu %>% rgl::plot3d() 10 | 11 | # my own mesh 12 | vertices <- c( 13 | -100.0, -100.0, 0, 100.0, 14 | 50.0, -50.0, 0, 50.0, 15 | 100.0, 100.0, 0, 100.0, 16 | -50.0, 50.0, 0, 50.0 17 | ) 18 | indices <- c( 1, 2, 3, 4 ) 19 | 20 | qmesh3d(vertices, indices) -> huup # %>% wire3d() 21 | 22 | #### jpeg to string? 23 | 24 | library(raster) 25 | 26 | fnu <- raster("data-raw/berries.png") 27 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # R for travis: see documentation at https://docs.travis-ci.com/user/languages/r 2 | 3 | language: R 4 | cache: packages 5 | 6 | matrix: 7 | include: 8 | - os: linux 9 | dist: trusty 10 | sudo: required 11 | env: R_CODECOV=true 12 | r_check_args: '--use-valgrind' 13 | 14 | addons: 15 | apt: 16 | packages: 17 | - valgrind 18 | - libirrlicht1.8 19 | - libirrlicht-dev 20 | 21 | warnings_are_errors: true 22 | 23 | r_packages: 24 | - covr 25 | 26 | after_success: 27 | - Rscript -e 'library(covr); codecov()' 28 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: Rirrlicht 2 | Type: Package 3 | Title: Rirrlicht - Irrlicht engine API 4 | Version: 0.0.1 5 | Authors@R: c( 6 | person("Clemens", "Schmid", email = "clemens@nevrome.de", role = c("cre", "cph", "aut")) 7 | ) 8 | Maintainer: Clemens Schmid 9 | Description: A small proof of concept of an R API for the Irrlicht game engine. 10 | Date: 2016-10-14 11 | License: GPL-2 12 | LazyData: TRUE 13 | RoxygenNote: 6.0.1 14 | Imports: 15 | Rcpp (>= 0.12.7), 16 | magrittr (>= 1.5), 17 | Morpho (>= 2.5.1) 18 | Suggests: 19 | devtools (>= 1.12.0), 20 | knitr, 21 | rmarkdown (>= 1.0), 22 | roxygen2 (>= 5.0.1), 23 | testthat (>= 1.0.2) 24 | VignetteBuilder: knitr 25 | Depends: 26 | R (>= 3.3.0) 27 | LinkingTo: Rcpp 28 | -------------------------------------------------------------------------------- /src/vector.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | // vector struct 6 | typedef struct { double x, y, z; } v3; 7 | 8 | v3 v_add(v3 a, v3 b) { return (v3) { a.x + b.x, a.y + b.y, a.z + b.z }; } 9 | v3 v_sub(v3 a, v3 b) { return (v3) { a.x - b.x, a.y - b.y, a.z - b.z }; } 10 | v3 v_mul(v3 a, double b) { return (v3) { a.x * b, a.y * b, a.z * b }; } 11 | double v_dot(v3 a, v3 b) { return a.x * b.x + a.y * b.y + a.z * b.z; } 12 | double v_sqr(v3 a) { return a.x * a.x + a.y * a.y + a.z * a.z; } 13 | double v_len(v3 a) { return sqrt(v_sqr(a)); } 14 | v3 v_crs(v3 a, v3 b) { 15 | return (v3) { a.y * b.z - a.z * b.y, 16 | a.z * b.x - a.x * b.z, 17 | a.x * b.y - a.y * b.x 18 | }; 19 | } 20 | v3 v_nor(v3 a) { return v_mul(a, 1.0 / v_len(a)); } 21 | 22 | void printv(v3 v) { printf("%f, %f, %f", v.x, v.y, v.z); } -------------------------------------------------------------------------------- /playground/stackoverflow_example.R: -------------------------------------------------------------------------------- 1 | library(library) 2 | library(jpeg) 3 | library(tibble) 4 | library(rgl) 5 | 6 | download.file( 7 | url = 'https://upload.wikimedia.org/wikipedia/en/6/6d/Chewbacca-2-.jpg', 8 | destfile = "chewbacca.jpg", 9 | mode = 'wb' 10 | ) 11 | 12 | chewie <- readJPEG("chewbacca.jpg", native = TRUE) 13 | 14 | chewie_corners <- tribble( 15 | ~x, ~y, ~z, 16 | -2,-1,-3, 17 | 1,-1,-3, 18 | 1,1,2, 19 | -2,1,2 20 | ) 21 | 22 | coolpicture <- function(image, position) { 23 | rgl::show2d( { 24 | graphics::par(mar = rep(0, 4)) 25 | graphics::plot( 0:1, 0:1, type = "n", ann = FALSE, 26 | axes = FALSE, xaxs = "i", yaxs = "i" ) 27 | graphics::rasterImage(image, 0, 0, 1, 1) 28 | }, 29 | x = position[, 1], y = position[, 2], z = position[, 3] 30 | ) 31 | } 32 | 33 | rgl::plot3d( 34 | chewie_corners$x, 35 | chewie_corners$y, 36 | chewie_corners$z, col = "red", 37 | size = 10, 38 | axes = FALSE, 39 | xlab = "", ylab = "", zlab = "" 40 | ) 41 | coolpicture(chewie, chewie_corners) 42 | -------------------------------------------------------------------------------- /playground/rasters.R: -------------------------------------------------------------------------------- 1 | library(magrittr) 2 | library(tibble) 3 | 4 | images <- c("data-raw/berries.png") 5 | #images <- c("data-raw/berries.png", "data-raw/berries.png") 6 | images <- c("data-raw/berries.png", "data-raw/berries.png", "data-raw/berries.png") 7 | 8 | images_corners1 <- tribble( 9 | ~x, ~y, ~z, 10 | 0,0,0, 11 | 1,5,0, 12 | 1,5,2, 13 | 0,0,2 14 | ) %>% as.data.frame() 15 | 16 | # images_corners2 <- tribble( 17 | # ~x, ~y, ~z, 18 | # 0,0,0, 19 | # 0,2,0, 20 | # 0,2,2, 21 | # 0,0,2 22 | # ) %>% as.data.frame() 23 | 24 | images_corners3 <- tribble( 25 | ~x, ~y, ~z, 26 | 0,0,0, 27 | 3,0,0, 28 | 3,5,5, 29 | 0,5,5 30 | ) %>% as.data.frame() 31 | 32 | images_corners2 <- tribble( 33 | ~x, ~y, ~z, 34 | 0,3,0, 35 | 5,-3,0, 36 | 5,-3,3, 37 | 0,3,3.01 38 | ) %>% as.data.frame() 39 | 40 | plot_irr( 41 | raster_paths_cv = images, 42 | #raster_corners_list = list(images_corners2), 43 | #raster_corners_list = list(images_corners1, images_corners3), 44 | raster_corners_list = list(images_corners1, images_corners2, images_corners3), 45 | video_driver = "d", 46 | doomhud = TRUE 47 | ) 48 | -------------------------------------------------------------------------------- /src/event_receiver.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace irr; 5 | using namespace core; 6 | using namespace scene; 7 | using namespace video; 8 | using namespace io; 9 | using namespace gui; 10 | 11 | IrrlichtDevice* plotdevice = 0; 12 | 13 | // event receiver class 14 | class MyEventReceiver : public IEventReceiver { 15 | public: 16 | virtual bool OnEvent(const SEvent& event) { 17 | // Remember whether each key is down or up 18 | if (event.EventType == EET_KEY_INPUT_EVENT){ 19 | KeyIsDown[event.KeyInput.Key] = event.KeyInput.PressedDown; 20 | } 21 | // closing event 22 | if (event.EventType == EET_KEY_INPUT_EVENT && event.KeyInput.Key == KEY_ESCAPE && event.KeyInput.PressedDown){ 23 | plotdevice->closeDevice(); 24 | } 25 | return false; 26 | } 27 | 28 | // This is used to check whether a key is being held down 29 | virtual bool IsKeyDown(EKEY_CODE keyCode) const { 30 | return KeyIsDown[keyCode]; 31 | } 32 | 33 | MyEventReceiver() { 34 | for (u32 i=0; i do not edit by hand 2 | # Generator token: 10BE3573-1514-4C36-9D1C-5A225CD40393 3 | 4 | #' Irrlicht 3D plot 5 | #' @description 6 | #' Creates a 3D plot using the Irrlicht engine. 7 | #' 8 | #' @param points_df optional - data.frame with coordinates and attributes 9 | #' of scattered points 10 | #' @param raster_paths_cv troooeeeet 11 | #' @param raster_corners_list huuuup 12 | #' @param mesh_cv floet 13 | #' @param doomhud fluet 14 | #' @param video_driver 15 | #' \itemize{ 16 | #' \item{"a": }{OPENGL (default)} 17 | #' \item{"b": }{DIRECT3D9} 18 | #' \item{"c": }{DIRECT3D8} 19 | #' \item{"d": }{BURNINGSVIDEO} 20 | #' \item{"e": }{SOFTWARE} 21 | #' \item{"f": }{NULL} 22 | #' } 23 | #' 24 | #' @return boolean value - side effect; irrlicht window is relevant 25 | #' 26 | #' @examples 27 | #' \dontrun{ 28 | #' plot_irr( 29 | #' points = data.frame( 30 | #' x = rnorm(500)*200, 31 | #' y = rnorm(500)*200, 32 | #' z = rnorm(500)*200, 33 | #' size = rnorm(500)*10 34 | #' ), 35 | #' raster_paths_cv = c("data-raw/berries.png") 36 | #' ) 37 | #' } 38 | #' 39 | #' @export 40 | plot_irr <- function(points_df = NULL, raster_paths_cv = NULL, raster_corners_list = NULL, mesh_cv = NULL, doomhud = FALSE, video_driver = 'a') { 41 | .Call('_Rirrlicht_plot_irr', PACKAGE = 'Rirrlicht', points_df, raster_paths_cv, raster_corners_list, mesh_cv, doomhud, video_driver) 42 | } 43 | 44 | -------------------------------------------------------------------------------- /src/rect.h: -------------------------------------------------------------------------------- 1 | #include "quaternion.h" 2 | 3 | // input r12angle struct (anti-clockwise) 4 | typedef struct { v3 p[4]; } r12; 5 | 6 | int r_valid(r12 r) { 7 | v3 e[4] = { v_sub(r.p[1], r.p[0]), v_sub(r.p[2], r.p[1]), 8 | v_sub(r.p[3], r.p[2]), v_sub(r.p[0], r.p[3]) }; 9 | if (fabs(v_len(e[0])-v_len(e[2])) > EPS || fabs(v_len(e[1])-v_len(e[3])) > EPS) 10 | return 0; 11 | if (fabs(v_dot(e[0],e[1])) > EPS || fabs(v_dot(e[1],e[2])) > EPS || 12 | fabs(v_dot(e[2],e[3])) > EPS || fabs(v_dot(e[3],e[0])) > EPS) 13 | return 0; 14 | v3 n[4] = { v_nor(v_crs(e[0],e[1])), v_nor(v_crs(e[1],e[2])), 15 | v_nor(v_crs(e[2],e[3])), v_nor(v_crs(e[3],e[0])) }; 16 | if (v_dot(n[0],n[1]) < 1 - EPS || v_dot(n[1],n[2]) < 1 - EPS || 17 | v_dot(n[2],n[3]) < 1 - EPS || v_dot(n[3],n[0]) < 1 - EPS) 18 | return 0; 19 | return 1; 20 | } 21 | v3 r_normal(r12 r) { return v_nor(v_crs(v_sub(r.p[1],r.p[0]),v_sub(r.p[3],r.p[0]))); } 22 | double r_width(r12 r) { return v_len(v_sub(r.p[3],r.p[0])); } 23 | double r_height(r12 r) { return v_len(v_sub(r.p[1],r.p[0])); } 24 | r12 r_get(q4 r, double w, double h) { 25 | w *= 0.5; h *= 0.5; 26 | return (r12) { q_rot(r, (v3) { -w, h, 0.0 }), 27 | q_rot(r, (v3) { -w, -h, 0.0 }), 28 | q_rot(r, (v3) { w, -h, 0.0 }), 29 | q_rot(r, (v3) { w, h, 0.0 }) 30 | }; 31 | } 32 | v3 r_ctr(r12 r) { return v_mul(v_add(r.p[0],v_add(r.p[1],v_add(r.p[2],r.p[3]))),0.25); } 33 | r12 r_add(r12 r, v3 a) { return (r12) {v_add(r.p[0],a),v_add(r.p[1],a),v_add(r.p[2],a),v_add(r.p[3],a)}; } 34 | r12 r_sub(r12 r, v3 a) { return (r12) {v_sub(r.p[0],a),v_sub(r.p[1],a),v_sub(r.p[2],a),v_sub(r.p[3],a)}; } 35 | -------------------------------------------------------------------------------- /src/RcppExports.cpp: -------------------------------------------------------------------------------- 1 | // Generated by using Rcpp::compileAttributes() -> do not edit by hand 2 | // Generator token: 10BE3573-1514-4C36-9D1C-5A225CD40393 3 | 4 | #include 5 | 6 | using namespace Rcpp; 7 | 8 | // plot_irr 9 | bool plot_irr(Nullable points_df, Nullable raster_paths_cv, Nullable raster_corners_list, Nullable mesh_cv, bool doomhud, char video_driver); 10 | RcppExport SEXP _Rirrlicht_plot_irr(SEXP points_dfSEXP, SEXP raster_paths_cvSEXP, SEXP raster_corners_listSEXP, SEXP mesh_cvSEXP, SEXP doomhudSEXP, SEXP video_driverSEXP) { 11 | BEGIN_RCPP 12 | Rcpp::RObject rcpp_result_gen; 13 | Rcpp::RNGScope rcpp_rngScope_gen; 14 | Rcpp::traits::input_parameter< Nullable >::type points_df(points_dfSEXP); 15 | Rcpp::traits::input_parameter< Nullable >::type raster_paths_cv(raster_paths_cvSEXP); 16 | Rcpp::traits::input_parameter< Nullable >::type raster_corners_list(raster_corners_listSEXP); 17 | Rcpp::traits::input_parameter< Nullable >::type mesh_cv(mesh_cvSEXP); 18 | Rcpp::traits::input_parameter< bool >::type doomhud(doomhudSEXP); 19 | Rcpp::traits::input_parameter< char >::type video_driver(video_driverSEXP); 20 | rcpp_result_gen = Rcpp::wrap(plot_irr(points_df, raster_paths_cv, raster_corners_list, mesh_cv, doomhud, video_driver)); 21 | return rcpp_result_gen; 22 | END_RCPP 23 | } 24 | 25 | static const R_CallMethodDef CallEntries[] = { 26 | {"_Rirrlicht_plot_irr", (DL_FUNC) &_Rirrlicht_plot_irr, 6}, 27 | {NULL, NULL, 0} 28 | }; 29 | 30 | RcppExport void R_init_Rirrlicht(DllInfo *dll) { 31 | R_registerRoutines(dll, NULL, CallEntries, NULL, NULL); 32 | R_useDynamicSymbols(dll, FALSE); 33 | } 34 | -------------------------------------------------------------------------------- /src/quaternion.h: -------------------------------------------------------------------------------- 1 | #include "vector.h" 2 | 3 | #define EPS 1e-10 4 | 5 | // quaternion struct 6 | typedef struct { union { struct { double x, y, z; }; v3 n; }; double w; } q4; 7 | 8 | q4 q_crt(v3 n, double a) { 9 | a*=0.5; 10 | q4* res = new q4; 11 | res->n = v_mul(v_nor(n),sin(a)); 12 | res->w = cos(a); 13 | return (*res); 14 | } 15 | 16 | q4 q_nor(q4 a) { 17 | double b = 1.0 / sqrt(v_sqr(a.n) + a.w * a.w); 18 | q4* res = new q4; 19 | res->n = v_mul(a.n, b); 20 | res->w = a.w * b; 21 | return (*res); 22 | } 23 | 24 | double clamp(double a) { return (a > 1.0) ? 1.0 : ((a < -1.0) ? -1.0 : a);} 25 | q4 q_btw(v3 a, v3 b) { 26 | a = v_nor(a); b = v_nor(b); 27 | double d = v_dot(a, b); 28 | if (d >= 1.0) { 29 | q4* res = new q4; 30 | res->x = 0.0; 31 | res->y = 0.0; 32 | res->z = 0.0; 33 | res->w = 1.0; 34 | return (*res); 35 | } else if (d <= -1.0) { 36 | v3 axis = v_crs((v3){1.0, 0.0, 0.0}, a); 37 | if (v_len(axis) < EPS) { axis = v_crs((v3){0.0, 1.0, 0.0}, a); } 38 | q4* res = new q4; 39 | res->n = v_nor(axis); 40 | res->w = 0.0; 41 | return (*res); 42 | } 43 | double s = sqrt(2.0*(1+d)), i = 1.0 / s; 44 | 45 | q4* res = new q4; 46 | res->n = v_mul(v_crs(a,b), i); 47 | res->w = 0.5 * s; 48 | return (q_nor(*res)); 49 | } 50 | q4 q_inv(q4 a) { 51 | q4* res = new q4; 52 | res->n = a.n; 53 | res->w = -a.w; 54 | return (*res); 55 | } 56 | q4 q_mul(q4 a, q4 b) { 57 | return (q4) { 58 | a.w * b.x + a.x * b.w + a.y * b.z - a.z * b.y, 59 | a.w * b.y - a.x * b.z + a.y * b.w + a.z * b.x, 60 | a.w * b.z + a.x * b.y - a.y * b.x + a.z * b.w, 61 | a.w * b.w - a.x * b.x - a.y * b.y - a.z * b.z 62 | }; 63 | } 64 | v3 q_rot(q4 r, v3 a) { 65 | return (v3) { a.x*(r.x*r.x+r.w*r.w-r.y*r.y- r.z*r.z) + a.y*(2*r.x*r.y- 2*r.w*r.z) + a.z*(2*r.x*r.z+ 2*r.w*r.y), 66 | a.x*(2*r.w*r.z + 2*r.x*r.y) + a.y*(r.w*r.w - r.x*r.x+ r.y*r.y - r.z*r.z)+ a.z*(-2*r.w*r.x+ 2*r.y*r.z), 67 | a.x*(-2*r.w*r.y+ 2*r.x*r.z) + a.y*(2*r.w*r.x+ 2*r.y*r.z)+ a.z*(r.w*r.w - r.x*r.x- r.y*r.y+ r.z*r.z) 68 | }; 69 | } 70 | 71 | void printq(q4 q) { printf("%f, %f, %f, %f", q.x, q.y, q.z, q.w); } -------------------------------------------------------------------------------- /src/geom_vector_calc.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "rect.h" 6 | 7 | using namespace Rcpp; 8 | 9 | using namespace irr; 10 | using namespace core; 11 | using namespace scene; 12 | using namespace video; 13 | using namespace io; 14 | using namespace gui; 15 | 16 | core::vector3df position_calc(DataFrame rcdf) { 17 | 18 | NumericVector x = rcdf["x"]; 19 | NumericVector y = rcdf["y"]; 20 | NumericVector z = rcdf["z"]; 21 | 22 | core::vector3df pos( 23 | sum(x) / 4, 24 | sum(y) / 4, 25 | sum(z) / 4 26 | ); 27 | 28 | Rcout << "Image position: " << pos.X << ", " << pos.Y << ", " << pos.Z << std::endl; 29 | 30 | return pos; 31 | } 32 | 33 | core::vector3df scale_calc(DataFrame rcdf) { 34 | 35 | NumericVector x = rcdf["x"]; 36 | NumericVector y = rcdf["y"]; 37 | NumericVector z = rcdf["z"]; 38 | 39 | core::vector3df lu(x(0), y(0), z(0)); 40 | core::vector3df ru(x(1), y(1), z(1)); 41 | core::vector3df lo(x(3), y(3), z(3)); 42 | 43 | core::vector3df scale( 44 | lu.getDistanceFrom(lo), 45 | lu.getDistanceFrom(ru), 46 | 0 47 | ); 48 | 49 | Rcout << "Image scale: " << scale.X << ", " << scale.Y << ", " << scale.Z << std::endl;; 50 | 51 | return scale * 0.1; 52 | } 53 | 54 | vector3df rotation_calc(DataFrame rcdf) { 55 | // transform DataFrame to r12 56 | NumericVector x = rcdf["x"]; 57 | NumericVector y = rcdf["y"]; 58 | NumericVector z = rcdf["z"]; 59 | r12 rcdf2 = { 60 | (v3) {x[0], y[0], z[0]}, 61 | (v3) {x[1], y[1], z[1]}, 62 | (v3) {x[2], y[2], z[2]}, 63 | (v3) {x[3], y[3], z[3]} 64 | }; 65 | // check if r12 is valid (correct point order) 66 | if (!r_valid(rcdf2)) { Rcpp::Rcout << "Corner coordinates of image are not valid. Rotation may be wrong." << std::endl ; } 67 | // subtract center offset 68 | rcdf2 = r_sub(rcdf2, r_ctr(rcdf2)); 69 | // p quaternion (between normals) 70 | q4 p = q_btw((v3) { 0.0, 0.0, 1.0 }, r_normal(rcdf2)); 71 | // mid-point calculations 72 | v3 mab = (v3) { 0.0, 1.0, 0.0 }; 73 | v3 u = q_rot(p, mab); 74 | v3 mde = v_mul(v_add(rcdf2.p[0],rcdf2.p[3]), 0.5); 75 | // q quaternion 76 | q4 q = q_btw(mab, mde); 77 | // multiplication 78 | q4 r = q_mul(p, q); 79 | // transform quaternion to degrees 80 | quaternion f(r.x, r.y, r.z, r.w); 81 | vector3df euler; 82 | f.toEuler(euler); 83 | vector3df degrees( 84 | euler.X * (180.0 / M_PI), 85 | euler.Y * (180.0 / M_PI), 86 | euler.Z * (180.0 / M_PI) 87 | ); 88 | 89 | Rcout << "Image rotation degrees: " << degrees.X << ", " << degrees.Y << ", " << degrees.Z << std::endl; 90 | 91 | return degrees; // i swapped the order on this operation 92 | } -------------------------------------------------------------------------------- /src/plot_irr.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "event_receiver.h" 4 | #include "geom_vector_calc.h" 5 | 6 | using namespace Rcpp; 7 | using namespace irr; 8 | using namespace core; 9 | using namespace scene; 10 | using namespace video; 11 | using namespace io; 12 | using namespace gui; 13 | 14 | //' Irrlicht 3D plot 15 | //' @description 16 | //' Creates a 3D plot using the Irrlicht engine. 17 | //' 18 | //' @param points_df optional - data.frame with coordinates and attributes 19 | //' of scattered points 20 | //' @param raster_paths_cv troooeeeet 21 | //' @param raster_corners_list huuuup 22 | //' @param mesh_cv floet 23 | //' @param doomhud fluet 24 | //' @param video_driver 25 | //' \itemize{ 26 | //' \item{"a": }{OPENGL (default)} 27 | //' \item{"b": }{DIRECT3D9} 28 | //' \item{"c": }{DIRECT3D8} 29 | //' \item{"d": }{BURNINGSVIDEO} 30 | //' \item{"e": }{SOFTWARE} 31 | //' \item{"f": }{NULL} 32 | //' } 33 | //' 34 | //' @return boolean value - side effect; irrlicht window is relevant 35 | //' 36 | //' @examples 37 | //' \dontrun{ 38 | //' plot_irr( 39 | //' points = data.frame( 40 | //' x = rnorm(500)*200, 41 | //' y = rnorm(500)*200, 42 | //' z = rnorm(500)*200, 43 | //' size = rnorm(500)*10 44 | //' ), 45 | //' raster_paths_cv = c("data-raw/berries.png") 46 | //' ) 47 | //' } 48 | //' 49 | //' @export 50 | // [[Rcpp::export]] 51 | bool plot_irr( 52 | Nullable points_df = R_NilValue, 53 | Nullable raster_paths_cv = R_NilValue, 54 | Nullable raster_corners_list = R_NilValue, 55 | Nullable mesh_cv = R_NilValue, 56 | bool doomhud = false, 57 | char video_driver = 'a' 58 | ){ 59 | 60 | // driver selection 61 | E_DRIVER_TYPE driverType; 62 | switch (video_driver) { 63 | case 'a': driverType = video::EDT_OPENGL; break; 64 | case 'b': driverType = video::EDT_DIRECT3D9; break; 65 | case 'c': driverType = video::EDT_DIRECT3D8; break; 66 | case 'd': driverType = video::EDT_BURNINGSVIDEO; break; 67 | case 'e': driverType = video::EDT_SOFTWARE; break; 68 | case 'f': driverType = video::EDT_NULL; break; 69 | default: return 1; 70 | } 71 | 72 | // start up the engine 73 | plotdevice = createDevice( 74 | driverType, 75 | core::dimension2d(1000,700) 76 | ); 77 | 78 | // test if engine is running 79 | if (!plotdevice) {return true;} 80 | 81 | // initialize videodriver, scenemanager and guienvironment 82 | video::IVideoDriver* driver = plotdevice->getVideoDriver(); 83 | scene::ISceneManager* scenemgr = plotdevice->getSceneManager(); 84 | IGUIEnvironment* guienv = plotdevice->getGUIEnvironment(); 85 | 86 | // set window title 87 | plotdevice->setWindowCaption(L"irrlicht - Plot"); 88 | // make cursor invisible 89 | plotdevice->getCursorControl()->setVisible(false); 90 | 91 | // create event receiver for closing with escape 92 | plotdevice->setEventReceiver(new MyEventReceiver()); 93 | 94 | 95 | // add stuff to scene 96 | 97 | // add points 98 | if (points_df.isNotNull()) { 99 | 100 | DataFrame points = as(points_df); 101 | 102 | // coordinates 103 | NumericVector x = points["x"]; 104 | NumericVector y = points["y"]; 105 | NumericVector z = points["z"]; 106 | 107 | // check for other variables 108 | CharacterVector points_attributes = points.attr("names"); 109 | 110 | // define default point size 111 | NumericVector size = rep(NumericVector::create(1), points.nrow()); 112 | 113 | for (auto pas : points_attributes) { 114 | // set point size from input 115 | if (pas == "size") { 116 | size = points["size"]; 117 | } 118 | } 119 | 120 | // create first scenenode from first point 121 | scene::ISceneNode* node = 122 | scenemgr->addSphereSceneNode(size(0), 16, 0, -1, core::vector3df(x(0),y(0),z(0))); 123 | 124 | // add scenenodes for every following point 125 | for (int i=1; i 127 | addSphereSceneNode(size(i), 16, 0, -1, core::vector3df(x(i),y(i),z(i))); 128 | } 129 | } 130 | 131 | // add lines 132 | 133 | // TODO? 134 | 135 | // add rasters 136 | if (raster_paths_cv.isNotNull() && raster_corners_list.isNotNull()) { 137 | 138 | //TODO: check, if length if path cv and corners_list is identical 139 | 140 | // transform input to not nullable 141 | CharacterVector raster_paths_cv_not_nullable = as(raster_paths_cv); 142 | List raster_corners_list_not_nullable = as(raster_corners_list); 143 | 144 | // loop to deal with every picture 145 | for(int i = 0; i < raster_paths_cv_not_nullable.size(); i++) { 146 | 147 | // extract DataFrames with corner positions from List -> ugly transformation 148 | SEXP rcdf_sexp = raster_corners_list_not_nullable[i]; 149 | DataFrame rcdf = as(rcdf_sexp); 150 | 151 | // plot corner points 152 | NumericVector x = rcdf["x"]; 153 | NumericVector y = rcdf["y"]; 154 | NumericVector z = rcdf["z"]; 155 | for (int u = 0; u < x.size(); u++) { 156 | scenemgr-> 157 | addSphereSceneNode(0.3, 16, 0, -1, core::vector3df(x(u),y(u),z(u))); 158 | } 159 | 160 | // calc position and scale vector 161 | vector3df position = position_calc(rcdf); 162 | vector3df scale = scale_calc(rcdf); 163 | vector3df degrees = rotation_calc(rcdf); 164 | 165 | // prepare raster image 166 | std::string blubb = as(raster_paths_cv_not_nullable[i]); 167 | core::string blubb2 = blubb.c_str(); 168 | 169 | // add raster to scene 170 | scene::ISceneNode * picturenode = scenemgr->addCubeSceneNode(); 171 | 172 | if (picturenode) { 173 | // position: mean of coordinates 174 | picturenode->setPosition(position); 175 | // scale 176 | picturenode->setScale(core::vector3df(scale)); 177 | // rotation 178 | picturenode->setRotation(degrees); 179 | // texture 180 | picturenode->setMaterialTexture(0, driver->getTexture(blubb2)); 181 | // light (off) 182 | picturenode->setMaterialFlag(video::EMF_LIGHTING, false); 183 | } 184 | } 185 | } 186 | 187 | // add meshes 188 | if (mesh_cv.isNotNull()) { 189 | 190 | //convert from Nullable to CharacterVector 191 | CharacterVector mesh_cv_not_nullable = as(mesh_cv); 192 | 193 | // loop through all strings 194 | for (auto mesh_string_pointer : mesh_cv_not_nullable) { 195 | 196 | //write string to temporary file 197 | std::string mesh_string = Rcpp::as(mesh_string_pointer); 198 | const void * a = mesh_string.c_str(); 199 | char filename[] = "/tmp/mytemp.XXXXXX.ply"; 200 | int fd = mkstemps(filename, 4); 201 | if (fd == -1) return 1; 202 | if (write(fd, a, mesh_string.length()) == -1) { 203 | Rcout << "Error in the creation of a temporary file.\n"; 204 | } 205 | 206 | //load mesh from file and add it to the scene 207 | scene::ISceneNode* node = 208 | scenemgr->addAnimatedMeshSceneNode(scenemgr->getMesh(filename)); 209 | 210 | // close file connection and remove temporary file 211 | close(fd); 212 | unlink(filename); 213 | 214 | // if everything worked, add a texture and disable lighting 215 | // if (node) { 216 | // node->setMaterialTexture(0, driver->getTexture(pathtexturestr.c_str())); 217 | // node->setMaterialFlag(video::EMF_LIGHTING, false); 218 | // } 219 | } 220 | } 221 | 222 | // add doomhud 223 | if (doomhud) { 224 | float width = driver->getViewPort().getWidth(); 225 | float height = driver->getViewPort().getHeight(); 226 | 227 | ITexture* tex = driver->getTexture("data-raw/doomhud.png"); 228 | 229 | IGUIImage* img; 230 | 231 | img = guienv->addImage(core::rect(0, 400, width, height)); 232 | img->setImage(tex); 233 | img->setScaleImage(true); 234 | driver->removeTexture(tex); 235 | } 236 | 237 | // define font 238 | gui::IGUIFont* font = plotdevice->getGUIEnvironment()->getBuiltInFont(); 239 | 240 | //coordinate system text 241 | // scene::IBillboardTextSceneNode* bill = 0; 242 | // bill = scenemgr->addBillboardTextSceneNode( 243 | // font, L"0|0|0", 244 | // node, dimension2d(200, 50), 245 | // vector3df(100,100,100) 246 | // ); 247 | // bill->setTextColor(video::SColor(255,0,0,0)); 248 | 249 | // add a first person shooter style user controlled camera 250 | // Key map added to allow multiple keys for actions such as 251 | // movement. ie Arrow keys & W,S,A,D. 252 | SKeyMap keyMap[8]; 253 | keyMap[0].Action = EKA_MOVE_FORWARD; 254 | keyMap[0].KeyCode = KEY_UP; 255 | keyMap[1].Action = EKA_MOVE_FORWARD; 256 | keyMap[1].KeyCode = KEY_KEY_W; 257 | 258 | keyMap[2].Action = EKA_MOVE_BACKWARD; 259 | keyMap[2].KeyCode = KEY_DOWN; 260 | keyMap[3].Action = EKA_MOVE_BACKWARD; 261 | keyMap[3].KeyCode = KEY_KEY_S; 262 | 263 | keyMap[4].Action = EKA_STRAFE_LEFT; 264 | keyMap[4].KeyCode = KEY_LEFT; 265 | keyMap[5].Action = EKA_STRAFE_LEFT; 266 | keyMap[5].KeyCode = KEY_KEY_A; 267 | 268 | keyMap[6].Action = EKA_STRAFE_RIGHT; 269 | keyMap[6].KeyCode = KEY_RIGHT; 270 | keyMap[7].Action = EKA_STRAFE_RIGHT; 271 | keyMap[7].KeyCode = KEY_KEY_D; 272 | 273 | ICameraSceneNode *camera = scenemgr->addCameraSceneNodeFPS( 274 | 0, 100.0f, 0.01f, -1, keyMap, 8 275 | ); 276 | 277 | // global light 278 | //scenemgr->setAmbientLight(video::SColorf(0.3,0.3,0.3,1)); 279 | 280 | // draw everything (run-loop) 281 | while (plotdevice->run() && driver) { 282 | // set scene with background color 283 | driver->beginScene(true, true, video::SColor(255,255,255,255)); 284 | // add fixed text 285 | if (font) { 286 | font->draw(L"This is a plot.", 287 | core::rect(130,10,300,50), 288 | video::SColor(255,0,0,0)); 289 | } 290 | // draw coordinate system axis 291 | // driver->draw3DLine(vector3df(-1000, 0, 0), vector3df(1000, 0, 0), SColor(255, 0, 0, 255)); //rot 292 | // driver->draw3DLine(vector3df(0, -1000, 0), vector3df(0, 1000, 0), SColor(0, 255, 0, 255)); //grün 293 | // driver->draw3DLine(vector3df(0, 0, -1000), vector3df(0, 0, 1000), SColor(0, 0, 255, 255)); //blau 294 | // draw nodes and gui 295 | scenemgr->drawAll(); 296 | guienv->drawAll(); 297 | // end scene 298 | driver->endScene(); 299 | } 300 | 301 | // delete plotdevice 302 | plotdevice->drop(); 303 | 304 | return true; 305 | } --------------------------------------------------------------------------------