Commit 75f569cd authored by Arnaud Blanchard's avatar Arnaud Blanchard

Merge branch 'master' of https://git.u-cergy.fr/blaar/blaar into HEAD

parents aae759a3 0815f0ae
Pipeline #291 passed with stage
in 7 minutes and 17 seconds
.install_script: &install_definition
script:
- apt-get update
- apt-get install -y wget sudo
- wget -O - https://git.u-cergy.fr/blaar/blaar/raw/master/developer_tools/clone_and_install_blaar_ubuntu.sh | bash
build:xenial:
image: ubuntu:xenial
<<: *install_definition
build:bionic:
image: ubuntu:bionic
<<: *install_definition
build:cosmic:
image: ubuntu:cosmic
<<: *install_definition
# Ignore disco for now.
#build:disco:
# image: ubuntu:disco
# <<: *install_definition
\ No newline at end of file
Integration with blc_loop
=========================
This is for synchronization, for more information about blc_channels, see ( https://git.u-cergy.fr/blaar/blibs/blc_channel/blob/master/README.md ).
output channel
--------------
Example defining a synchronous (':', '/' for asynchronous) channel of unsigned char ('UIN8') of 4096 values with no specific format ('NDEF').
Example defining a synchronous (':' double synchronization, '/' for asynchronization) channel of unsigned char ('UIN8') of 4096 values with no specific format ('NDEF').
#include "blc_channel.h"
#include "blc_program.h"
......
2017 July 29 Arnaud Blanchard
Applications
------------
* blc_channels: remove unused channel has been fixed
* o_gtk_image [FL32,Y800] add min/max optio] to scale the pixel values
* o_gtk_image display the name of the channel in the window's tilte
* o_gnuplot fix a problem using history with synchronized channel. There is still a small problem while display the time scale
* scripts New script **e_profile.sh** which displays the graphs of the profiling of differents apps: i.e. 'e_profile.sh image.tsv gradient.tsv display.tsv' ( see profile option of blc_program)
Libraries
---------
* blc_channels fix snchronisation problems
* blc_program in you can use command with parameter in one line (i.e. in blc_channels i3 display image of channel 3)
* blc_program add option --profile<filename.tsv> (-~) which record the stat of the execution loop. It can then be displayed with **e_profile.sh**
2017 July 29 Arnaud Blanchard
Applications
------------
* blc_channels: remove unused channel has been fixed
* o_gtk_image [FL32,Y800] add min/max option to scale the pixel values
* o_gtk_image display the name of the channel in the window's tilte
* o_gnuplot fix a problem using history with synchronized channel. There is still a small problem while display the time scale
* New script **e_profile.sh** which displays the graphs of the profiling of differents apps: i.e. 'e_profile.sh image.tsv gradient.tsv display.tsv' ( see profile option of blc_program)
* Add documentation (`PROFILING.md`) axplaining how to use the new profile option.
Libraries
---------
* blc_channels fix synchronisation problems
* blc_program in you can use command with parameter in one line (i.e. in blc_channels i3 display image of channel 3)
* blc_program add option --profile<filename.tsv> (-~) which record the stat of the execution loop. It can then be displayed with **e_profile.sh**
Install blaar on Mac OSX
========================
[Video tutorial](http://www.etis.ensea.fr/neurocyber/blaar/videos/blaar_osx_install.mp4)
You need Xcode or a C/C++ compilation (clang, g++, ..).
Copy past this line in a terminal
curl -sS https://promethe.u-cergy.fr/blaar/blaar/raw/master/developer_tools/clone_and_install_blaar_osx.sh | bash &&
cd blaar
curl -sS https://git.u-cergy.fr/blaar/blaar/raw/master/developer_tools/clone_and_install_blaar_osx.sh | bash
You can do [**step by step**](https://promethe.u-cergy.fr/blaar/blaar/blob/master/developer_tools/clone_and_install_blaar_osx.sh) copying command line by command line. This may be useful to better understand and in case where the installer crash.
You can do [**step by step**](https://git.u-cergy.fr/blaar/blaar/blob/master/developer_tools/clone_and_install_blaar_osx.sh) copying command line by command line. This may be useful to better understand and in case where the installer crash.
If you have a problem at compile time it may be due to a bad configuration of your compiler [**see how to install command line tools mac os x**](http://osxdaily.com/2014/02/12/install-command-line-tools-mac-os-x/)
\ No newline at end of file
......@@ -3,11 +3,9 @@ Install blaar on Ubuntu or Raspbian (rasperry pi)
Copy past this line in a terminal
wget -q -O/tmp/blaar_install.sh https://promethe.u-cergy.fr/blaar/blaar/raw/master/developer_tools/clone_and_install_blaar_ubuntu.sh &&
bash /tmp/blaar_install.sh; rm /tmp/blaar_install.sh; cd blaar
wget -O - https://git.u-cergy.fr/blaar/blaar/raw/master/developer_tools/clone_and_install_blaar_ubuntu.sh | bash
You will be requested at some point to enter your admin password to make the installation.
You can do [**step by step**](https://promethe.u-cergy.fr/blaar/blaar/blob/master/developer_tools/clone_and_install_blaar_ubuntu.sh) copying command line by command line. This may be useful to better understand and in case where the installer crash.
You can do [**step by step**](https://git.u-cergy.fr/blaar/blaar/blob/master/developer_tools/clone_and_install_blaar_ubuntu.sh) copying command line by command line. This may be useful to better understand and in case where the installer crash.
[![logo](logo_blaar.png)](http://blaar.org)
[![logo](logo_blaar_square.png)](http://blaar.org)
Basic Libraries And Applications for Robotics
=============================================
......@@ -9,7 +9,7 @@ The modules are minimalist in purpose. If you want more you are advised to use s
BLAAR is **open source** CeCILL v2.1 (compatible GPL v2).
**BLAAR** should work on **POSIX** systems. GNU/Linux (**Ubuntu/Raspbian**), Darwin (**Mac OSX**) and Bionic (**Android** not usable yet).
**BLAAR** is still under development, it is **not stable yet** but feel free to [ask improvement or notify issues](https://promethe.u-cergy.fr/blaar/blaar/issues).
**BLAAR** is still under development, it is **not stable yet** but feel free to [ask improvement or notify issues](https://git.u-cergy.fr/blaar/blaar/issues).
Install
=======
......@@ -22,32 +22,38 @@ Modules
Most of these modules are installed by default (otherwise see below for installation)
- [bapps](https://promethe.u-cergy.fr/blaar/bapps.git): Basic Applications for C/C++ with no dependency to manipulate blc_channels (find max, generate oscillators, ...). (no requirement)
- [gnuplot](https://promethe.u-cergy.fr/blaar/gnuplot.git) Allows to make graphs from data of blc_channels (require gnuplot)
- [gtk](https://promethe.u-cergy.fr/blaar/gtk.git) Provides a graphical user interface. For now, it displays images. (require gtk)
- [png](https://promethe.u-cergy.fr/blaar/png.git) Saves and loads png images. (require libpng)
- [sndfile](https://promethe.u-cergy.fr/blaar/sndfile.git) Reads and writes sound files in with many sound formats (require libsndfile)
- [bapps](https://git.u-cergy.fr/blaar/bapps.git): Basic Applications for C/C++ with no dependency to manipulate blc_channels (find max, generate oscillators, ...). (no requirement)
- [gnuplot](https://git.u-cergy.fr/blaar/gnuplot.git) Allows to make graphs from data of blc_channels (require gnuplot)
- [gtk](https://git.u-cergy.fr/blaar/gtk.git) Provides a graphical user interface. For now, it displays images. (require gtk)
- [png](https://git.u-cergy.fr/blaar/png.git) Saves and loads png images. (require libpng)
- [sndfile](https://git.u-cergy.fr/blaar/sndfile.git) Reads and writes sound files in with many sound formats (require libsndfile)
Specific Linux
--------------
- [asound](https://promethe.u-cergy.fr/blaar/asound.git) Acquire or produce sound using Advanced Linux Sound Architecture library (require libasounddev).
- [i_v4l2_camera](https://promethe.u-cergy.fr/blaar/i_v4l2_camera.git) Acquire images from a camera using Video 4 Linux library (require libv4l2dev).
- [asound](https://git.u-cergy.fr/blaar/asound.git) Acquire or produce sound using Advanced Linux Sound Architecture library (require libasounddev).
- [i_v4l2_camera](https://git.u-cergy.fr/blaar/i_v4l2_camera.git) Acquire images from a camera using Video 4 Linux library (require libv4l2dev).
Specific Mac OSX
----------------
- [coreaudio](https://promethe.u-cergy.fr/blaar/asound.git) Acquire or produce sound using Apple Core Audio Frameworks
- [i_AV_camera](https://promethe.u-cergy.fr/blaar/i_AV_camera.git) Acquire images from cameras using Audio Video Foundation
- [coreaudio](https://git.u-cergy.fr/blaar/asound.git) Acquire or produce sound using Apple Core Audio Frameworks
- [i_AV_camera](https://git.u-cergy.fr/blaar/i_AV_camera.git) Acquire images from cameras using Audio Video Foundation
Specific raspberry pi
---------------------
The Linux of raspberry (raspbian) can be used like ubuntu but it has more functionalities to manage input outputs that why there is a specific package.
- [raspi](https://promethe.u-cergy.fr/blaar/raspi.git)
- [raspinobo](https://promethe.u-cergy.fr/blaar/raspinobo.git) Specific for the robotic head
- [raspi](https://git.u-cergy.fr/blaar/raspi.git)
- [raspinobo](https://git.u-cergy.fr/blaar/raspinobo.git) Specific for the robotic head
In development
--------------
- [blc_server](https://git.u-cergy.fr/blaar/blc_server.git) Create a server to monitor **blc_channels**
- [blpy_channel](https://git.u-cergy.fr/blaar/blibs/blpy_channel.git) Python module to interact with the blc_channel in python
Usage
=====
......@@ -59,7 +65,7 @@ You will only have basic framework. You may add submodules as needed. To add mod
Example, install 'fftw' package:
git submodule add https://promthe.u-cergy.fr/blaar/fftw.git
git submodule add https://git.u-cergy.fr/blaar/fftw.git
./install.sh fftw
You need to have installed the required package. Usually indicated in the README.
......@@ -93,10 +99,10 @@ You can freely mix blibs and low level C/C++ functions.
### blc are basic libraries for C/C++ with no specific requirement
- [blc_core](https://promethe.u-cergy.fr/blibs/blc_core) : Generic helpers in C or C++ used by all other blaar projects
- [blc_channel](https://promethe.u-cergy.fr/blibs/blc_channel) : Tools to use shared memory in synchrone ( in test) and asynchrone mode
- [blc_image](https://promethe.u-cergy.fr/blibs/blc_image) : Manipulate blc_array as images. Can load and save them as png files
- [blc_program](https://promethe.u-cergy.fr/blibs/blc_program) : Parse arguments and interacts with user in commandline.
- [blc_core](https://git.u-cergy.fr/blaar/blibs/blc_core/blob/master/README.md) : Generic helpers in C or C++ used by all other blaar projects
- [blc_channel](https://git.u-cergy.fr/blaar/blibs/blc_channel/blob/master/README.md) : Tools to use shared memory in synchrone ( in test) and asynchrone mode
- [blc_image](https://git.u-cergy.fr/blaar/blibs/blc_image/blob/master/README.md) : Manipulate blc_array as images. Can load and save them as png files
- [blc_program](https://git.u-cergy.fr/blaar/blibs/blc_program/blob/master/README.md) : Parse arguments and interacts with user in commandline.
To get details about compilation and documentation of the functions, see [developement](DEVELOPMENT.md).
......
#ifdef __APPLE__
#include <OpenGL/gl3.h>
#else
#include <GL/glew.h>
#endif
#include "blc_core.h"
#include "blgl_viewer.h"
int *shaders_ids=NULL;
int shaders_nb=0;
//shader_type=GL_VERTEX_SHADER|GL_FRAGMENT_SHADER
GLuint blgl_create_shader(char const *source, GLenum shader_type){
char *log_buffer;
GLuint shader_id;
shader_id = glCreateShader(shader_type);
GLint result, log_length;
glShaderSource(shader_id, 1, &source , NULL);
glCompileShader(shader_id);
APPEND_ITEM(&shaders_ids, &shaders_nb, &shader_id);
// Check Vertex Shader
glGetShaderiv(shader_id, GL_COMPILE_STATUS, &result);
glGetShaderiv(shader_id, GL_INFO_LOG_LENGTH, &log_length);
if ( log_length){
log_buffer=MANY_ALLOCATIONS(log_length+1, char);
glGetShaderInfoLog(shader_id, log_length, NULL, log_buffer);
fprintf(stderr, "%s\n", log_buffer);
FREE(log_buffer);
}
if (result==GL_FALSE) EXIT_ON_ERROR("Compiling shader '%s'.", source);
return shader_id;
}
/* You need to create the shader ( blgl_create_shader ) before */
GLint blgl_create_program(){
char *log_buffer;
GLint result, log_length, program_id;
int i;
if (shaders_nb==0) EXIT_ON_ERROR("You need to create at least a shader before");
program_id = glCreateProgram();
FOR(i, shaders_nb){
glAttachShader(program_id, shaders_ids[i]);
}
glLinkProgram(program_id);
// Check the program
glGetProgramiv(program_id, GL_LINK_STATUS, &result);
glGetProgramiv(program_id, GL_INFO_LOG_LENGTH, &log_length);
if (log_length > 0 ){
log_buffer=MANY_ALLOCATIONS(log_length+1, char);
glGetProgramInfoLog(program_id, log_length, NULL, log_buffer);
fprintf(stderr, "%s\n", log_buffer);
FREE(log_buffer);
if (result==0) EXIT_ON_ERROR("Linking GL program");
}
FOR(i, shaders_nb){
glDetachShader(program_id, shaders_ids[i]);
glDeleteShader(shaders_ids[i]);
}
FREE(shaders_ids);
shaders_nb=0;
return program_id;
}
# Copyright ETIS — ENSEA, Université de Cergy-Pontoise, CNRS (2011 - 2018)
# Author: Arnaud Blanchard
# This software is governed by the CeCILL v2.1 license under French law and abiding by the rules of distribution of free software.
# You can use, modify and/ or redistribute the software under the terms of the CeCILL v2.1 license as circulated by CEA, CNRS and INRIA at the following URL "http://www.cecill.info".
# As a counterpart to the access to the source code and rights to copy, modify and redistribute granted by the license,
# users are provided only with a limited warranty and the software's author, the holder of the economic rights, and the successive licensors have only limited liability.
# In this respect, the user's attention is drawn to the risks associated with loading, using, modifying and/or developing or reproducing the software by the user in light of its specific status of free software,
# that may mean that it is complicated to manipulate, and that also therefore means that it is reserved for developers and experienced professionals having in-depth computer knowledge.
# Users are therefore encouraged to load and test the software's suitability as regards their requirements in conditions enabling the security of their systems and/or data to be ensured  and, more generally, to use and operate it in the same conditions as regards security.
# The fact that you are presently reading this means that you have had knowledge of the CeCILL v2.1 license and that you accept its terms.
#Basic Library for robotique simulator with gem
cmake_minimum_required(VERSION 2.6)
project(blglm_simulator)
find_package(blc_core REQUIRED)
find_package(glm REQUIRED)
set(sources blglm_simulator.cpp)
set(DEFINITIONS ${BL_DEFINITIONS})
set(INCLUDES ${BL_INCLUDE_DIRS} ${GLM_INCLUDE_DIRS} include)
set(LIBRARIES ${BL_LIBRARIES})
message(include: ${GLM_INCLUDE_DIRS})
add_definitions(${DEFINITIONS})
include_directories(${INCLUDES})
add_library(blglm_simulator SHARED ${sources})
target_link_libraries(blglm_simulator ${LIBRARIES})
#static_version
add_library(static_blglm_simulator STATIC ${sources})
set_target_properties(static_blglm_simulator PROPERTIES OUTPUT_NAME blglm_simulator)
#Describe what will be to install or in the package
install(DIRECTORY include/ DESTINATION include/blglm_simulator)
install(TARGETS blglm_simulator static_blglm_simulator DESTINATION lib)
install(FILES blglm_simulator-config.cmake DESTINATION share/blglm_simulator)
find_package(blc_core REQUIRED)
find_package(glm REQUIRED)
find_path(BLGLM_SIMULATOR_INCLUDE_DIR blglm_simulator.h PATH_SUFFIXES blglm_simulator)
find_library(BLGLM_SIMULATOR_LIBRARY blglm_simulator)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(blglm_simulator DEFAULT_MSG BLGLM_SIMULATOR_INCLUDE_DIR BLGLM_SIMULATOR_LIBRARY)
#No access outide this file
mark_as_advanced(BLGLM_SIMULATOR_INCLUDE_DIR BLGLM_SIMULATOR_LIBRARY)
list(APPEND BL_INCLUDE_DIRS ${BLGLM_SIMULATOR_INCLUDE_DIR} ${GLM_INCLUDE_DIRS})
list(APPEND BL_LIBRARIES ${BLGLM_SIMULATOR_LIBRARY} ${BL_LIBRARIES})
list(REMOVE_DUPLICATES BL_INCLUDE_DIRS)
list(REMOVE_DUPLICATES BL_LIBRARIES)
//
// opengl.cpp
// tino_viewer
//
// Created by Arnaud Blanchard on 29/11/2017.
//
//
#include "blglm_simulator.h"
#include "blc_core.h" //APPEND_ITEM, FOR, FREE...
#include <glm/glm.hpp>
#include <glm/gtx/euler_angles.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtx/quaternion.hpp>
int blglm_simulator_joint::root_joints_nb=0;
blglm_simulator_joint **blglm_simulator_joint::root_joints=NULL;
blglm_simulator_joint::blglm_simulator_joint(blglm_simulator_joint *support, glm::vec3 pos, glm::vec3 axis, float *angle_pt ,float limit_min, float limit_max)
:angle_pt(angle_pt), limit_min(limit_min), limit_max(limit_max), pos(pos), axis(axis), joints(NULL), joints_nb(0){
blglm_simulator_joint *this_joint=this;
if (support) APPEND_ITEM(&support->joints, &support->joints_nb, &this_joint);
else APPEND_ITEM(&root_joints, &root_joints_nb, &this_joint);
}
/*
blgl_joint::blgl_ball_joint(blgl_joint *support, glm::vec3 pos, float *angle_pt ,float limit_min, float limit_max)
:angle_pt(angle_pt), limit_min(limit_min), limit_max(limit_max), pos(pos), joints(NULL), joints_nb(0){
blgl_joint *this_joint=this;
if (support) APPEND_ITEM(&support->joints, &support->joints_nb, &this_joint);
else APPEND_ITEM(&root_joints, &root_joints_nb, &this_joint);
}*/
blglm_simulator_joint::~blglm_simulator_joint(){
int i;
FOR(i, joints_nb) delete joints[i];
FREE(joints);
}
void blglm_simulator_joint::update(glm::mat4 current_model){
float absolute_angle;
glm::quat quaternion; //We need to use quaternion to have local rotation
int i;
if (*angle_pt>1 || *angle_pt<0) EXIT_ON_ERROR("Value of angle '%f' outside the range [0,1]", *angle_pt);
absolute_angle=*angle_pt*(limit_max-limit_min)+limit_min;
current_model = glm::translate(current_model, pos);
quaternion=glm::angleAxis(absolute_angle, axis);
model=current_model*glm::toMat4(quaternion);
FOR(i, joints_nb) joints[i]->update(model);
}
void blglm_simulator_joint::update_all(){
int i;
glm::mat4 identity;
FOR(i, root_joints_nb) root_joints[i]->update(identity);
}
void blglm_simulator_joint::remove_all(){
int i;
FOR(i, root_joints_nb) delete root_joints[i];
FREE(root_joints);
}
#ifndef BLGLM_SIMULATOR_H
#define BLGLM_SIMULATOR_H
#include <glm/glm.hpp>
typedef struct blglm_simulator_joint{
float *angle_pt, limit_min, limit_max;
glm::mat4 model;
glm::vec3 pos, axis;
blglm_simulator_joint **joints;
int joints_nb;
static blglm_simulator_joint **root_joints;
static int root_joints_nb;
blglm_simulator_joint(blglm_simulator_joint *support, glm::vec3 pos, glm::vec3 axis, float *angle_pt, float limit_min=-M_PI, float limit_max=M_PI);
// blgl_ball_joint(blgl_joint *support, glm::vec3 pos, float angles_pt[3], float limit_min=-M_PI, float limit_max=M_PI);
~blglm_simulator_joint();
void update(glm::mat4 model);
static void update_all();
static void remove_all();
}blglm_simulator_joint;
#endif /* _3DMODEL_H */
......@@ -34,11 +34,11 @@ hash brew || {
}
echo
# if install fail it may be because it needs upgrade
brew install git cmake ninja doxygen libpng libjpeg gnuplot --with-qt fftw libsndfile || brew upgrade git cmake ninja doxygen libpng libjpeg gnuplot --with-qt fftw libsndfile #note:only git, cmake and ninja are really required
brew install git cmake ninja doxygen libpng libjpeg gnuplot fftw libsndfile || brew upgrade git cmake ninja doxygen libpng libjpeg gnuplot fftw libsndfile #note:only git, cmake and ninja are really required
echo
echo "Download blaar sources"
echo
git clone https://promethe.u-cergy.fr/blaar/blaar.git
git clone https://git.u-cergy.fr/blaar/blaar.git
echo
cd blaar
echo
......@@ -46,8 +46,8 @@ developer_tools/clone_standard_modules.sh
echo
echo "Clone specific modules"
echo
git submodule add https://promethe.u-cergy.fr/blaar/coreaudio.git
git submodule add https://promethe.u-cergy.fr/blaar/i_AV_camera.git
git submodule add https://git.u-cergy.fr/blaar/coreaudio.git
git submodule add https://git.u-cergy.fr/blaar/i_AV_camera.git
echo
echo "Install and test each module"
......
......@@ -28,11 +28,12 @@ echo
echo
echo "Install required packages"
echo
sudo apt-get update
sudo apt-get install git g++ cmake ninja-build doxygen lsof libpng-dev libjpeg-dev gnuplot fftw3-dev libsndfile1-dev libasound2-dev libv4l-dev libgtk-3-dev #Only git g++ cmake ninja-build are really required
echo
echo "Download sources"
echo
git clone https://promethe.u-cergy.fr/blaar/blaar.git
git clone https://git.u-cergy.fr/blaar/blaar.git
echo
cd blaar
echo
......@@ -40,12 +41,13 @@ developer_tools/clone_standard_modules.sh
echo
echo Clone specific modules
echo
git submodule add https://promethe.u-cergy.fr/blaar/asound.git
git submodule add https://promethe.u-cergy.fr/blaar/i_v4l2_camera.git
git submodule add https://git.u-cergy.fr/blaar/asound.git
git submodule add https://git.u-cergy.fr/blaar/i_v4l2_camera.git
echo
echo "Install and check each project"
echo
./install.sh all
sudo ldconfig #This is to update the cache of libraries with newly installed libraries in /usr/local/lib
./check_all.sh
echo
echo "Update your bash profile"
......
......@@ -18,23 +18,12 @@ source scripts/blaar.sh
#blc_core has to be first. It is used by all others projects
#blc_program has to be before blc_processes ( not nice dependecy)
lib_website=https://promethe.u-cergy.fr/blibs
module_website=https://promethe.u-cergy.fr/blaar
basic_libs="blc_core blc_channel blc_network blc_program blc_process blc_image"
standard_modules="blc_channels bapps gnuplot fftw sndfile png gtk"
# We do not use function in purpose as 'set -o errexit' has no effect in it
for lib in $basic_libs
do
echo "Cloning '$lib':"
commandline="git submodule add $lib_website/$lib.git blibs/$lib"
$commandline &> $TMP_DIR/clone_$lib.log || { echo "Fail cloning '$lib' executing: '$commandline'"; cat $TMP_DIR/clone_$lib.log; echo ; }
done
website=https://git.u-cergy.fr/blaar
standard_modules="blibs/blc_core blibs/blc_channel blibs/blc_network blibs/blc_program blibs/blc_process blibs/blc_image blc_channels bapps gnuplot fftw sndfile png gtk"
for module in $standard_modules
do
echo "Cloning '$module':"
commandline="git submodule add $module_website/$module.git"
$commandline &> $TMP_DIR/clone_$module.log || { echo "Fail cloning '$module' executing: '$commandline'"; cat $TMP_DIR/clone_$module.log; echo ; }
commandline="git submodule add $website/$module.git $module"
$commandline &> $TMP_DIR/clone_`basename $module`.log || { echo "Fail cloning '$module' executing: '$commandline'"; cat $TMP_DIR/clone_`basename $module`.log; echo ; }
done
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment