Xedi.Xermawan's Blog

personal-technical blog

Archive for the ‘C/C++ Diary’ Category

Mencoba Cmake ( a Cmake intro )

leave a comment »

Visual Studio adalah favorite ide pemrograman saya. Sudah sejak lama saya pakai, kira-kira sejak 4 tahun yang lalu. Mungkin menjadi favorite karena kebiasaan ūüôā dan juga visual studio mudah dipakai. Walaupun kadang ada juga bugs ‘aneh’ yang terjadi selama nge-build project dengan VS. Sayangnya, ‘build file’ dari VS tidak bisa dipakai di environment lain, misalnya mau build pakai gcc di Linux. Oleh karena itulah harus ada cara lain yang harus dipakai. Berikut sebagai contoh case saya :

Misalnya saya memiliki super advanced top secret project dengan informasi sebagai berikut :

[1] struktur file-file project :

              ->project_root_dir

              ->HelloSource.cpp file

              ->PrintLib dir

                      ->PrintHello.h file

             ->PrintHello.cpp file

[2] project diatas akan di-build dengan output sebagai berikut :

¬† ¬† ¬† ¬† ¬† ¬†-HelloSource.cpp —> executable file , yang menggunakan PrintLib (static linked)

¬† ¬† ¬† ¬† ¬† ¬†-PrintLib —> static library

          -Penggunaan PrintLib bisa di ON-OFF melalui macro ( o yeah, macro define, I like it )

[3] source code : HelloSource.cpp

          /* hello apps project */

          #include <iostream>

¬† ¬† ¬† ¬† ¬† #include “HelloVersion.h”

          #ifdef USE_PRINTLIB

¬† ¬† ¬† ¬† ¬† #include “PrintHello.h”

          #endif

          using namespace std;

          int main(char* argc, char** argv)

          {

¬† ¬† ¬† ¬† ¬†¬† ¬† ¬† ¬† ¬† cout << “hello ” << endl;

¬† ¬† ¬† ¬† ¬†¬† ¬† ¬† ¬† ¬† cout <<” hello version : “<< Hello_VERSION_MAJOR <<“-“<<Hello_VERSION_MINOR<<endl;

                   #ifdef USE_PRINTLIB

                   PrintHelloFunc();

                   #endif

                   return 0;

          }

[4] source code : PrintHello.h

          #include <iostream>

          void PrintHelloFunc();

[5] source code : PrintHello.cpp

¬† ¬† ¬† ¬† ¬† #include “PrintHello.h”

          void PrintHelloFunc()

          {

¬† ¬† ¬† ¬† ¬†¬† ¬† ¬† ¬† ¬† std::cout << ” hey hello i’m here. printed from hellolib ” << std::endl;

          }

Pertanyaannya, Bagaimana agar project diatas bisa build-able beberapa platform dengan beda compiler ?

Banyak cara, dari cara manual ( buat build script untuk masing-masing platform ) atau dengan tools misalnya : autotools, scons, jam, waf, cmake.yang terakhir: cmake, saya sering mem-build project dari internet yang menggunakan cmake, jadi lebih kenal dengan yang satu ini. Kenyataannya cmake sudah secara luas digunakan di opensource project sejak lama. Build sistem yang lain yang pernah saya coba, dan sangat mudah (dari segi user yang akan mem-build project tersebut) adalah build sistem dari boost library : boost-jam . sangat mudah nge-buildnya.

Kali ini saya tertarik mempelajari bagaimana membuat cmake file. cmake file itu semacam “template” make file atau meta make file. cmake file adalah file text yang berisi deskripsi project dengan syntax cmake dan disimpan dengan nama CMakeLists.txt. Kalau cmake file suatu project sudah dibuat, file ini bisa digunakan untuk meng-generate real make file yang bisa dipakai compiler. tergantung compiler apa yang dipakai . beberapa yang didukung :

          -Visual C++ ( sln & vcxproj ),

          -Kdevelop3, Eclipse, XCode,

          -makefiles (Unix,NMake, Borland, Watcom, MinGW, MSYS,Cygwin)

          -Code::Blocks

Dari daftar diatas, yang pernah saya coba adalah generate VS (solution & project) dan makefile untuk linux.

Dari deskripsi project diatas saya bisa membuat cmake file dengan struktur :

          ->project_root_dir

          ->CMakeLists.txt file                   cmake ke-1 (main)

          ->PrintLib dir

                   ->CMakeLists.txt file          cmake ke-2

          ->HelloVersion.h.in file untuk setting Define

 source file CMakeLists.txt ke-2 : ( hanya 1 baris )

          add_library(PrintLib PrintHello.cpp)

source file CMakeLists.txt ke-1 :

          #this is a cmake comment

          cmake_minimum_required (VERSION 2.6)

          #project name

          project (Hello)

          #version

          set (Hello_VERSION_MAJOR 1)

          set (Hello_VERSION_MINOR 0)

          #option can be turned on/off when generate real make file

¬† ¬† ¬† ¬† ¬† option ( USE_PRINTLIB “use print library” ON )

          configure_file (

¬† ¬† ¬† ¬† ¬†¬† ¬† ¬† ¬† ¬† “${PROJECT_SOURCE_DIR}/HelloVersion.h.in”

¬† ¬† ¬† ¬† ¬†¬† ¬† ¬† ¬† ¬† “${PROJECT_BINARY_DIR}/HelloVersion.h”

          )

¬† ¬† ¬† ¬† ¬† include_directories (“${PROJECT_BINARY_DIR}”)

          if (USE_PRINTLIB)

¬† ¬† ¬† ¬† ¬†¬† ¬† ¬† ¬† ¬† include_directories (“${PROJECT_SOURCE_DIR}/PrintLib”)

                   add_subdirectory(PrintLib)

                   set (EXTRA_LIBS ${EXTRA_LIBS} PrintLib)

          endif (USE_PRINTLIB)

          #tell cmake that Hello is executable

          add_executable(Hello HelloSource.cpp)

          #tell cmake that Hello linked with extralibs ( printlib )

          target_link_libraries(Hello ${EXTRA_LIBS})

          install(TARGETS Hello DESTINATION bin)

source file HelloVersion.h.in :

          // version for Hello project

          #define Hello_VERSION_MAJOR @Hello_VERSION_MAJOR@

          #define Hello_VERSION_MINOR @Hello_VERSION_MINOR@

          #cmakedefine USE_PRINTLIB

file tambahan adalah HelloVersion.h.in untuk mengatur version dan macro define. Kalau dibaca deskripsi output project saya diatas dan apa yang ada di cmakelist.txt semuanya tampak self explanatory. Dari CMakeLists.txt ini kita bisa meng-generate solution & project di windows & makefile di linux ( sebagai 2 test case untuk mencoba ) . di Ms-Windows bisa memakai cmake GUI.

cmake_flow

cmake_flow

Cmake file yang dipakai diatas adalah yang paling sederhana. Terdapat beberapa perintah yang lain, misalnya find_package, find_library, export, dan lainnya .

edie // 13022015 // Jakarta

Written by XediXermawan

February 13, 2015 at 4:30 pm

my calendar maker

with one comment

walaupun tahun baru masih 1 1/2 bulan lagi, entah dapat¬†dari mana ide-nya, saya tertarik untuk membuat kalender. Mungkin pikiran saya secara spontan kembali ke masa lalu ketika di tahun pertama kuliah dapat tugas membuat program kalender abadi¬†he he. ¬†Yang saya buat disini tidak jauh beda : program untuk membuat kalender, dengan input tahun dan bulan , output nya berupa image kalender. Jadi image nya bisa dipakai, atau mungkin di print ūüôā . Dan program ini dimaksudkan bisa membuat kalender tahun berapa pun, termasuk tahun 1, bulan 1. Walaupun doeloe pernah buat, saya sudah lupa gimana buatnya. sudah lama sekali.¬†Setelah search tentang kalender di wiki , berikut beberapa hal yang perlu diketahui, untuk menghitung hari dari tahun 1 :

[1] tahun 1, bulan 1, tanggal 1 adalah hari Sabtu.

[2] dari tahun 1 sampai sekarang, ada dua 2 sistem perhitungan kalender yang pernah dipakai : Julian kalender & Gregorian kalender. Sistem Julian kalender dipakai tahun 1 sampai dengan tahun 1581 . tahun 1582 sampai sekarang pakai sistem Gregorian kalender.

[3] jumlah bulan di Julian & Gregorian kalender tidak berbeda (mulai januari :31, 28/29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 )  . Bulan Februari 29 hari jika tahun tersebut kabisat (leap year) . jadi 1 tahun bisa 365 hari atau 366 hari .

[4] di Julian kalender, sebuah tahun dikatakan kabisat, jika tahun tersebut habis dibagi 4. artinya tahun kabisat selalu berulang setiap 4 tahun.

[5] di Gregorian kalender, tahun dikatakan kabisat jika tahun tersebut habis dibagi 4, tapi jika tahun tersebut habis di bagi 100, untuk disebut tahun kabisat, tahun tersebut harus habis juga di bagi 400. jadi tahun 8 adalah kabisat, tahun 1900 bukan kabisat. pseudo-code untuk menentukan ini :  ( source here ) :

if (year is not divisible by 4) then (it is a common year)
else
if (year is not divisible by 100) then (it is a leap year)
else
if (year is not divisible by 400) then (it is a common year)
else (it is a leap year)

ternyata perhitungan tahun-kabisat yang tricky ini, sudah menimbulkan banyak bug di berbagai software ¬†ūüôā

[6] di tahun 1582, saat pergantian dari sistem Julian ke Gregorian, ada pemotongan 10 hari !! di bulan Desember. Bulan Desember 1582 , berakhir tanggal 21, keesokan harinya tahun baru tanggal 1 1583 .

dengan info diatas, hari bisa dihitung dengan benar. Di program pembuat kalender ini, saya masukkan juga pasaran jawa ( Pon, Pahing, Wage, Kliwon, Legi ) :d . ini penting soalnya digunakan mbah/ibu saya untuk pergi ke pasar :-p . Hasil kalendar saya cetak di gambar berformat png. Bisa juga menggunakan png image yang sudah ada untuk dijadikan background . Bagaimana dengan hari libur ? . pengaturan hari libur juga dimasukkan dan bisa diatur di config file . program beroperasi dengan 2 type, mencetak 1 tahun penuh atau 1 bulan saja. contoh hasil program untuk mencetak 1 bulan ( tahun 1945, bulan 8 ) :

http://pngwriter.sourceforge.net/

hari Minggu otomatis berwarna merah dan Jum’at berwarna hijau. sedangkan hari-hari libur yang di-setting akan berwarna merah. default warna font dan jenis font bisa di atur di config file . penggunaan program di console sebagai berikut :

E:\test>ccm config2.ini

kalau tertarik mencoba , binary bisa didownload  di: LINK DOWNLOAD 1

dependencies code : pngwrite –> libpng–>zlib ( untuk berurusan dengan png ), dan inih (untuk parsing ini file )

main program sebagai berikut :

/*
*  console calendar maker ( ccm )
*  console app to generate calendar from year-month input, the output is png image
*  (c) 2014  edi ermawan < edi.ermawan@gmail.com >
*/

#include <iostream>
#include <stdio.h>
#include "png.hpp"
#include <pngwriter.h>
#include <sstream>
#include <string>
#include "color.h"
#include "ini.h"

using namespace std;

#define MOONINYEAR 12
#define DAYSINWEEK 7
#define DAY_SPACING "   "
#define DAY_COUNTED_SPACING "  "
#define CALOUT "calendar_out.png"

static char* day_name[DAYSINWEEK] = { "Su", "Mo", "Tu", "We", "Th", "Fr", "Sa" };
static char* month_name[MOONINYEAR] = { "January", "February", "March", "April", "May",
							        "June", "July","August","September","October",
							        "November","December"};
static char* pasaran_name[5] = { " Wage ","Kliwon "," Legi ", "Pahing", " Pon  " };

static const int day_in_month[MOONINYEAR] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };

struct s_printer {
	int print_type;
	int year_target;
	int month_target;
	char* background_img;
	int font_size;
	int pasaran_font_size;
	int moon_title_font_size;
	char* font_name;
	int start_x;
	int start_y;
	int pasaran_adjust_x;
	int pasaran_adjust_y;
	int row_space;
	int col_space;
	int month_horizontal_space;
	int month_vertical_space;
	int month_footer_space;
	int default_r;
	int default_g;
	int default_b;
	int img_w;
	int img_h;
};

struct s_holiday {
	int month;
	int day;
	char* info;
};

s_printer printer;
s_holiday holidays[80];
int holiday_index = 0;

bool is_holiday(int month, int day) {
	for (int i = 0; i < holiday_index; i++) {
		if (holidays[i].month == month && holidays[i].day == day) {
			return true;
		}
	}
	return false;
}

bool is_leap_year_1(int year) {
	if ( (year % 4 == 0) )
		return true;
	return false;
}

bool is_leap_year_2( int year ) {
	if ( ((year % 4 == 0) && (year % 100)) || (year % 400 == 0) )
		return true;
	return false;
}

bool is_leap_year(int year) {
	if (year > 1581) {
		return is_leap_year_2(year);
	}
	else {
		return is_leap_year_1(year);
	}
}

int get_month_length(bool leap_year, int nth_month) {
	if (nth_month != 1) {
		return day_in_month[nth_month];
	}
	if (leap_year)
		return 29;
	return 28;
}

int get_days_until_month(bool leap_year,int n) {
	int days = 0;
	while (n--)
		days += get_month_length(leap_year, n);
	return days;
}

int get_days_in_year(int y) {
	bool _leap_year = is_leap_year(y);
	return get_days_until_month(_leap_year, MOONINYEAR);
}

int get_days_until_year(int y) {
	int days = 0;
	for (int i = 1; i <= y; i++){
		days += get_days_in_year(i);
	}
	if (y<1582)
		return days;
	else
		return days-10;
}

void print_text(pngwriter& pw, char* text, int pos_x, int pos_y, s_color color) {
	pw.plot_text(printer.font_name, printer.font_size, pos_x, (printer.img_h - pos_y),
		0, text, color.r, color.g, color.b);
}

void print_text( pngwriter& pw, char* text, int pos_x, int pos_y ) {
	print_text(pw, text, pos_x, pos_y, s_color(printer.default_r, printer.default_g, printer.default_b,1.0f) );
}

s_color define_day_color(int week_index,int day_index, int total_day) {
	s_color color = c_color::Black;
	if (week_index == 0) {
		if (total_day == 7 && day_index == 0)
			color = c_color::Red;
		else if (day_index == total_day - 2)
			color = c_color::Green;
	}
	else if (day_index == 0) {
		color = c_color::Red;
	}
	else if (day_index == 5) {
		color = c_color::Green;
	}
	return color;
}

void print_aday(pngwriter& pw, char* text, int pos_x, int pos_y, s_color color,int p_index) {
	int _nday = atoi(text);
	if (_nday < 10) {
		char ntext[3]; ntext[0] = '0'; ntext[1] = text[0]; ntext[2] = '\0';
		print_text(pw, ntext, pos_x, pos_y, color);
	}
	else {
		print_text(pw, text, pos_x, pos_y, color);
	}
	pw.plot_text(printer.font_name, printer.pasaran_font_size, pos_x + printer.pasaran_adjust_x,
		(printer.img_h - pos_y) - printer.pasaran_adjust_y,
		0, pasaran_name[p_index], color.r, color.g, color.b);
}

void print_days(char** days, int week_index, int day_total, pngwriter& pw, int pos_x,
	int pos_y, int* pasaran, int curr_month) {
	int px = pos_x;
	if (week_index == 0) {
		int _space_day = DAYSINWEEK - day_total;
		for (int i = 0; i < _space_day; i++) {
			print_text(pw, "  ", px, pos_y + (week_index * printer.row_space) );
			px += (printer.col_space * 2);
			print_text(pw, DAY_SPACING, px, pos_y + (week_index * printer.row_space) );
			px += (printer.col_space * 2);
		}
	}
	for (int i = 0; i < day_total; i++) {
		s_color color = define_day_color(week_index, i, day_total);
		if( is_holiday(curr_month, atoi(days[i])) ) {
			color = c_color::Red;
		}
		print_aday(pw, days[i], px, pos_y + (week_index * printer.row_space), color, pasaran[i]);
		px += (printer.col_space * 2);
		print_text(pw, DAY_SPACING, px, pos_y + (week_index * printer.row_space));
		px += (printer.col_space * 2);
	}
}

void process_text(pngwriter& pw, int week_index, char* text, int pos_x, int pos_y, int* pasaran, int curr_month) {
	int _day_index  = 0;
	int _day_digit = 0;
	char _day_text[3];
	char* _days_text[DAYSINWEEK];
	while ( *text!='\0' ) {
		if ( *text != ' ' ) {
			_day_text[0] = *text++;
			_day_text[1] = *text++;
			_day_text[2] = '\0';
			_days_text[_day_index] = new char[3];
			memcpy(_days_text[_day_index++], _day_text, 3);
		}
		else {
			*text++;
		}
	}
	print_days(_days_text, week_index, _day_index, pw, pos_x, pos_y, pasaran, curr_month);

	for (int j = 0; j < _day_index; j++) {
		delete _days_text[j];
	}
}

int print_month_header(pngwriter& pw, int year, int month, int px, int py) {
	stringstream _ssmy;
	_ssmy << month_name[month - 1];
	_ssmy << " ";
	_ssmy << year;
	print_text(pw, const_cast<char*>( _ssmy.str().c_str() ), px, py);
	py = py + (printer.row_space);
	for (int i = 0; i < 7; i++) {
		print_text(pw, day_name[i], px, py );
		px += (printer.col_space * 2);
		print_text(pw, DAY_SPACING, px, py );
		px += (printer.col_space * 2);
	}
	return py;
}

void print_month_footer(pngwriter& pw, int month, int px, int py) {
	stringstream _ssfooter;
	int _n = 0;
	for (int i = 0; i < holiday_index; i++) {
		if (holidays[i].month == month) {
			_ssfooter << holidays[i].day;
			_ssfooter << " : ";
			_ssfooter << holidays[i].info;
			_ssfooter << "  ";
			_n++;
			if (_n == 2) {
				_n = 0;
				pw.plot_text(printer.font_name, printer.pasaran_font_size, px, (printer.img_h - py), 0,
					const_cast<char*>(_ssfooter.str().c_str()), c_color::Blue.r, c_color::Blue.g, c_color::Blue.b);
				_ssfooter.str("");
				_ssfooter.clear();
				py += printer.month_footer_space;
			}
		}
	}
	pw.plot_text(printer.font_name, printer.pasaran_font_size, px, (printer.img_h - py), 0,
		const_cast<char*>(_ssfooter.str().c_str()), c_color::Blue.r, c_color::Blue.g, c_color::Blue.b);
}

void print_month(pngwriter& pw, int year, int month, int total_days_prev_year, int ipx, int ipy ) {
	int _year_target = year;
	int _moon_target = month;

	int _printed_month = _moon_target - 1;
	bool _leapYear = is_leap_year(_year_target);
	int _prev_day = get_days_until_month(_leapYear, _printed_month) + total_days_prev_year;
	int _length_day_in_month = day_in_month[_printed_month];
	int _start_d = _prev_day + 1;
	int _end_d = _start_d + _length_day_in_month;

	int _week_index = 0;
	std::stringstream _stream;
	int px = ipx;
	int py = ipy;
	py = print_month_header(pw, _year_target, _moon_target, px, py);
	py = py + (printer.row_space);
	int* _pasaran = new int[7];
	int _pasaran_index = 0;
	for (int i = _start_d; i < _end_d; ++i) {
		int n = i - _prev_day;
		int mod = (i % 7);
		_pasaran[_pasaran_index++] = (i % 5);
		_stream << n << " ";
		if (mod == 1) {
			std::string s;
			s = _stream.str();
			process_text(pw, _week_index, const_cast<char*>(s.c_str()), px, py, _pasaran, _moon_target);
			_stream.str("");
			_stream.clear();
			_week_index++;
			_pasaran_index = 0;
		}
	}
	std::string s = _stream.str();
	process_text(pw, _week_index, const_cast<char*>(s.c_str()), px, py, _pasaran, _moon_target);
	if ( strcmp(s.c_str(), "") == 0 )
		_week_index -= 1;
	print_month_footer(pw, _moon_target, px, py+((_week_index )*printer.row_space)+printer.pasaran_adjust_y*3 );
	delete[] _pasaran;
}

void print_month(pngwriter& pw ,int year ,int month, int px, int py) {
	int _prev_day = get_days_until_year(year - 1);
	print_month( pw, year, month, _prev_day, px, py );
}

void print_year(pngwriter& pw, int year, int px, int py) {
	int ncol = 4;
	int nrow = 3;
	int nth_month = 1;

	int _prev_day = get_days_until_year(year - 1);
	int _px = px;
	int _py = py;
	for (int i = 0; i < nrow; i++) {
		for (int j = 0; j < ncol; j++) {
			print_month(pw, year, nth_month, _prev_day, _px, _py);
			nth_month++;
			_px += printer.month_horizontal_space;
		}
		_py += printer.month_vertical_space;
		_px = px;
	}
}

void add_holiday(int m, int d, char * info) {
	holidays[holiday_index].day = d;
	holidays[holiday_index].month = m;
	holidays[holiday_index].info = info;
	holiday_index++;
}

static int config_handler(void* user, const char* section, const char* name,
	const char* value)
{
	#define MATCH(s, n) strcmp(section, s) == 0 && strcmp(name, n) == 0
	#define MATCH2(s, n) strcmp(section, s) == 0

	s_printer* _pprinter = (s_printer*)(user);

	if (MATCH("setting", "print_type")) {
		_pprinter->print_type = atoi(value);
	}
	else if (MATCH("setting", "year_target")) {
		_pprinter->year_target = atoi(value);
	}
	else if (MATCH("setting", "month_target")) {
		_pprinter->month_target = atoi(value);
	}
	else if (MATCH("setting", "background_img")) {
		_pprinter->background_img = strdup(value);
	}
	else if ( MATCH("setting", "font_size") ) {
		_pprinter->font_size = atoi(value);
	}
	else if (MATCH("setting", "pasaran_font_size")) {
		_pprinter->pasaran_font_size = atoi(value);
	}
	else if (MATCH("setting", "moon_title_font_size")) {
		_pprinter->moon_title_font_size = atoi(value);
	}
	else if (MATCH("setting", "font_name")) {
		_pprinter->font_name = strdup(value);
	}
	else if (MATCH("setting", "start_x")) {
		_pprinter->start_x = atoi(value);
	}
	else if (MATCH("setting", "start_y")) {
		_pprinter->start_y = atoi(value);
	}
	else if (MATCH("setting", "pasaran_adjust_x")) {
		_pprinter->pasaran_adjust_x = atoi(value);
	}
	else if (MATCH("setting", "pasaran_adjust_y")) {
		_pprinter->pasaran_adjust_y = atoi(value);
	}
	else if (MATCH("setting", "row_space")) {
		_pprinter->row_space = atoi(value);
	}
	else if (MATCH("setting", "col_space")) {
		_pprinter->col_space = atoi(value);
	}
	else if (MATCH("setting", "month_horizontal_space")) {
		_pprinter->month_horizontal_space = atoi(value);
	}
	else if (MATCH("setting", "month_vertical_space")) {
		_pprinter->month_vertical_space = atoi(value);
	}
	else if (MATCH("setting", "month_footer_space")) {
		_pprinter->month_footer_space = atoi(value);
	}
	else if (MATCH("setting", "default_r")) {
		_pprinter->default_r = atoi(value);
	}
	else if (MATCH("setting", "default_g")) {
		_pprinter->default_g = atoi(value);
	}
	else if (MATCH("setting", "default_b")) {
		_pprinter->default_b = atoi(value);
	}
	else if (MATCH("setting", "img_w")) {
		_pprinter->img_w = atoi(value);
	}
	else if (MATCH("setting", "img_h")) {
		_pprinter->img_h = atoi(value);
	}
	// holidays parsing
	else if (MATCH2("January")) {
		add_holiday(1, atoi(name), strdup(value));
	}
	else if (MATCH2("February")) {
		add_holiday(2, atoi(name), strdup(value));
	}
	else if (MATCH2("March")) {
		add_holiday(3, atoi(name), strdup(value));
	}
	else if (MATCH2("April")) {
		add_holiday(4, atoi(name), strdup(value));
	}
	else if (MATCH2("May")) {
		add_holiday(5, atoi(name), strdup(value));
	}
	else if (MATCH2("June")) {
		add_holiday(6, atoi(name), strdup(value));
	}
	else if (MATCH2("July")) {
		add_holiday(7, atoi(name), strdup(value));
	}
	else if (MATCH2("August")) {
		add_holiday(8, atoi(name), strdup(value));
	}
	else if (MATCH2("September")) {
		add_holiday(9, atoi(name), strdup(value));
	}
	else if (MATCH2("October")) {
		add_holiday(10, atoi(name), strdup(value));
	}
	else if (MATCH2("November")) {
		add_holiday(11, atoi(name), strdup(value));
	}
	else if (MATCH2("December")) {
		add_holiday(12, atoi(name), strdup(value));
	}
	return 0;
}

int get_config(char* conf_name) {
	if (ini_parse(conf_name, config_handler, &printer) < 0) {
		printf("can't load file : %s\n", conf_name);
		return 1;
	}
	return 0;
}

int main(int argc, char** argv) {
	printf("console app to generate calendar from year - month input\n");
	printf("the output is png image\n");
	printf("(c) 2014 edi ermawan <edi.ermawan@gmail.com>\n");
	printf("use : ccm.exe configfilename.ini\n");
	if (argc > 1) {
		if ( get_config(argv[1]) ) {
			printf("error parse config file\n");
			return 1;
		}
	}
	else {
		printf("use : ccm.exe configfilename.ini");
		return 1;
	}

	pngwriter* _png;
	if ( strcmp(printer.background_img,"none") )  {
		_png = new pngwriter();
		_png->readfromfile( printer.background_img );
		_png->pngwriter_rename(CALOUT);
		printer.img_w = _png->getwidth();
		printer.img_h = _png->getheight();
	}
	else {
		_png = new pngwriter(printer.img_w, printer.img_h, 1.0, CALOUT);
	}
	if (printer.print_type == 1) {
		print_month(*_png, printer.year_target, printer.month_target, printer.start_x, printer.start_y);
		printf("done ..!");
	}
	else if (printer.print_type == 0) {
		print_year(*_png, printer.year_target, printer.start_x, printer.start_y);
		printf("done ..!");
	}
	_png->setcompressionlevel(6);
	_png->setgamma(0.7);
	_png->close();
	delete _png;

	return 0;
}

selanjutnya mungkin saya akan post full project ke svn google code / github .

// edi ermawan , Jkt 16/11/2014

Written by XediXermawan

November 16, 2014 at 4:13 pm

Rendering animated GIF file

with 3 comments

gif file ( http://en.wikipedia.org/wiki/Graphics_Interchange_Format ) is very popular format , you can share funny animation, emotion, a splash for ads, etc. All web browser support this format. do you interested in how game or application (non-web) render animated gif ? . here my experiment result :

( set video quality to 480p  )

info :
rendering : DirectX 11
language : c/c++
GIF decoder : libnsgif

Written by XediXermawan

May 30, 2014 at 12:31 am

Posted in C/C++ Diary

Tagged with , , ,

tcp client server dengan winsock2

leave a comment »

Untuk keperluan selanjutnya di pengembangan game yang sedang saya buat ( ūüėģ ), saya memerlukan program yang bisa di tweak lewat program lain. Dengan kata lain 2 program yang saling bisa berkomunikasi. Cara yang bisa dicapai , pakai file sharing atau dengan cara yang pas : pakai networking tcp / udp . Kalau mau buat program dengan kemampuan networking, cara yang bijaksana mungkin pakai 3rd party library yang sudah cukup populer dan reliable. Di antaranya, yang saya ketahui dan sepertinya sudah terbukti bagus : boost-asio ( free ), raknet ( free untuk hobyist ), libevent ( free ), etc. ada yang lain ?. Tapi saya pinginnya ndak usah pakai 3rd party lib, lagipula program yang akan saya buat cukup sederhana. winsock2 & c/c++ . itu pilihannya. dan rasanya akan fun juga buat program nya hehe. Sebagai pemanasan awal dengan winsock2 ( saya pemula ) , saya perlu membuat program seperti ini : ada client dan server. client bisa kirim data ke server. server bisa kirim data ke client. jika client nya lebih dari satu, client bisa kirim data ke client yang lain. representasi dari program ini adalah program “chatting” :d .
kemampuan server dalam keadaan running :
– stand by & listening kalau ada client mau membuat koneksi.
– setiap ada client tersambung, server akan membuat handler yang menangani koneksi ke client tersebut berdasarkan socket id. ( dalam test program dipost ini, setiap ada client, server membuat thread baru, untuk menanganinya ).
– server bisa kirim data ke client lewat console
– ada > 3 thread . 1 main thread: untuk menangkap command line dari user, 1 thread untuk “stand by” / connection acceptor, dan n thread , untuk n client yang tersambung. ya sebenarnya ndak benar ( bisa di optimize pakai thread pool mungkin nantinya ).

kemampuan client dalam keadaan running :
– connect ke server
– kirim data ke server
– ada 2 thread, 1 main thread, 1 client handler thread ( data receiver & sender )
– port hardcoded : 22536

catatan :
[1] saya membuat socket mode ke non-blocking , artinya pemanggilan fungsi2 winsock seperti connect, recv, send tidak akan mem-blok jalannya program loop. jadi non-blocking & di thread lain selain main thread.
u_long iMode = 1;  // iMode=0, blocking mode, default value
ioctlsocket(ConnectSocket, FIONBIO, &iMode);
[2] untuk sharing data antar thread, saya menggunakan concurrent queue dari blog Anthony William http://www.justsoftwaresolutions.co.uk/threading/implementing-a-thread-safe-queue-using-condition-variables.html yang saya modifikasi menggunakan std library ( awalnya pakai boost )
[3] data yang dikirim berupa string.

Beberapa pertanyaan :
– apa cara di catatan no [1] ( non-blocking & di thread lain ) efektif ?
– saat memakai non-blocking mode, kalau mengirim data byte yang sangat besaarr , apakah semuanya terkirim langsung ATAU setelah beberapa call sent?

Reference :
MSDN winsock2 ( belum semuanya saya baca¬†ūüėź¬†)

berikut code program chat client & server yang saya buat :

(server code) : ~288 baris

// xedi xermawan < edi.ermawan@gmail.com >
// May 27, 2014
// tcp client server networking test using winsock2
// server code

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <vector>
#include <queue>

#include "concurrent_queue.hpp"

#pragma comment (lib, "ws2_32.lib")

#define DEFAULT_BUFLEN 512
#define DEFAULT_PORT "22536"

struct XDclient_info
{
	int socket_id;
	struct sockaddr_in address;
};

class XDclient
{
public:
	XDclient(XDclient_info& clientinfo)
	{
		m_clientinfo = clientinfo;
		m_recvbuflen = DEFAULT_BUFLEN;
		m_islive = true;
		m_thread = new std::thread(&XDclient::run, this);
	}
	bool isLive()
	{
		return m_islive;
	}
	XDclient_info getinfo() const
	{
		return m_clientinfo;
	}
	~XDclient()
	{
		m_thread->join();
		delete m_thread;
	}
	std::string get_data()
	{
		std::string ret_str = "";
		if (!m_queue.empty())
		{
			m_queue.try_pop(ret_str);
		}
		return ret_str;
	}
private:
	XDclient_info m_clientinfo;
	std::thread* m_thread;
	char m_recvbuf[DEFAULT_BUFLEN];
	int m_recvbuflen;
	bool m_islive;
	concurrent_queue <std::string> m_queue;

	void run()
	{
		while (m_islive)
		{
			unsigned int result_recv = recv( m_clientinfo.socket_id, m_recvbuf, m_recvbuflen, 0);
			int nError = WSAGetLastError();
			if (nError != WSAEWOULDBLOCK&&nError != 0)
			{
				printf("Client Error: %d\n", nError);
				printf("Client disconnected: %d\n", nError);
				m_islive = false;
			}
			else if (result_recv != INVALID_SOCKET && result_recv!= 0)
			{
				m_recvbuf[result_recv] = '\0';
				std::string str_send(m_recvbuf);
				m_queue.push(str_send);
			}
			else if (result_recv == 0)
			{
				printf("Client disconnected: %d\n", nError);
				m_islive = false;
			}
			// give a sleep, avoid high percentage cpu usage
			Sleep(50);
		}
	}
};

class XDserver
{
public:
	XDserver()
	{
		m_recvbuflen = DEFAULT_BUFLEN;
		m_isrun = true;
		m_thread = new std::thread(&XDserver::run, this);
	}
	~XDserver()
	{
		m_isrun = false;
		m_thread->join();
		delete m_thread;
	}
	void run()
	{
		WSADATA wsaData;
		int result_ ;

		SOCKET listen_socket = INVALID_SOCKET;
		SOCKET client_socket = INVALID_SOCKET;

		struct addrinfo *result = NULL;
		struct addrinfo hints;

		char recvbuf[DEFAULT_BUFLEN];
		int recvbuflen = DEFAULT_BUFLEN;

		// init
		result_ = WSAStartup(MAKEWORD(2, 2), &wsaData);
		if (result_ != 0) {
			printf("WSAStartup failed with error: %d\n", result_);
		}

		ZeroMemory(&hints, sizeof(hints));
		hints.ai_family = AF_INET;
		hints.ai_socktype = SOCK_STREAM;
		hints.ai_protocol = IPPROTO_TCP;
		hints.ai_flags = AI_PASSIVE;

		// get info address for host
		result_ = getaddrinfo(NULL, DEFAULT_PORT, &hints, &result);
		if (result_ != 0) {
			printf("getaddrinfo failed with error: %d\n", result_);
			WSACleanup();
		}
		// create socket
		listen_socket = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
		if (listen_socket == INVALID_SOCKET) {
			printf("socket failed. error code : %ld\n", WSAGetLastError());
			freeaddrinfo(result);
			WSACleanup();
		}
		// set-up socket listening
		result_ = bind(listen_socket, result->ai_addr, (int)result->ai_addrlen);
		if (result_ == SOCKET_ERROR) {
			printf("bind failed. error code: %d\n", WSAGetLastError());
			freeaddrinfo(result);
			closesocket(listen_socket);
			WSACleanup();
			m_isrun = false;
		}
		if (!m_isrun)
			return;
		freeaddrinfo(result);

		result_ = listen(listen_socket, SOMAXCONN);
		if (result_ == SOCKET_ERROR) {
			printf("listen failed. error code: %d\n", WSAGetLastError());
			closesocket(listen_socket);
			WSACleanup();
		}
		// set to non-blocking mode
		u_long iMode = 1;
		ioctlsocket(listen_socket, FIONBIO, &iMode);
		int t_clientAddressLen = sizeof(struct sockaddr_in);
		struct sockaddr_in t_clientAddress;
		// server loop
		while (m_isrun)
		{
			// check new client connected
			client_socket = accept(listen_socket, (struct sockaddr *)&t_clientAddress, &t_clientAddressLen);
			int nError = WSAGetLastError();
			if (nError != WSAEWOULDBLOCK&&nError != 0)
			{
				// todo :
			}
			else if (client_socket != INVALID_SOCKET)
			{
				XDclient_info clientinfo;
				clientinfo.address = t_clientAddress;
				clientinfo.socket_id = client_socket;

				XDclient* clientnew = new XDclient(clientinfo);
				m_clients.push_back(clientnew);
				if (client_socket == INVALID_SOCKET) {
					printf("accept failed. error code: %d\n", WSAGetLastError());
				}
			}
			// check client live
			for (std::vector< XDclient* >::iterator it = m_clients.begin(); it != m_clients.end(); it++)
			{
				if (!(*it)->isLive())
				{
					delete *it;
					m_clients.erase(it);
					break;
				}
			}
			// check if there are data need to be sent
			if (!m_queue.empty())
			{
				std::string str = "";
				m_queue.try_pop(str );
				std::cout << " send data: " << str.c_str() <<std::endl;
				for (std::vector< XDclient* >::iterator it = m_clients.begin(); it != m_clients.end(); it++)
				{
					int m_recvbuflen;
					int sendresult = send((*it)->getinfo().socket_id, str.c_str(), str.length(), 0);
					if (sendresult == SOCKET_ERROR)
					{
						std::cout << " send failed. error code " << sendresult << std::endl;
					}
				}
			}
			for (std::vector< XDclient* >::iterator it = m_clients.begin(); it != m_clients.end(); it++)
			{
				std::string str_data = (*it)->get_data();
				if (str_data != "")
				{
					for (std::vector< XDclient* >::iterator it2 = m_clients.begin(); it2 != m_clients.end(); it2++)
					{
						if (*it != *it2)
						{
							int sendresult = send((*it2)->getinfo().socket_id, str_data.c_str(), str_data.length(), 0);
							if (sendresult == SOCKET_ERROR)
							{
								std::cout << " send failed. error code " << sendresult << std::endl;
							}
						}
					}
				}
			}
			// give a sleep, avoid high percentage cpu usage
			Sleep(50);
		}
	}
	void senddata(std::string& str)
	{
		m_queue.push(str);
	}
	bool isLive()
	{
		return m_isrun;
	}
private:
	char m_recvbuf[DEFAULT_BUFLEN];
	int m_recvbuflen;
	bool m_isrun;
	std::thread* m_thread;
	std::vector< XDclient* > m_clients;
	concurrent_queue <std::string> m_queue;
};

class Games
{
};

int __cdecl main(int argc, char** argv)
{
	XDserver* server = new XDserver();
	std::cout << "welcome to the server"<<std::endl;
	std::cout << "type an input to broadcast to all client. ""exit"" to close program " << std::endl;
	while (server->isLive())
	{
		char str_in[512]="";
		std::cin >> str_in;
		std::string str_input(str_in);
		server->senddata("server|"+str_input);
		if (!str_input.compare("exit"))
		{
			break;
		}
	}
	delete server;
	std::cout << "server shutdown" << std::endl;
	system("pause");
	return 0;
}

(client  code) :  ~181 baris

// xedi xermawan < edi.ermawan@gmail.com >
// May 27, 2014
// tcp client server networking test using winsock2
// client code

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <vector>
#include <queue>
#include <sstream>

#include "concurrent_queue.hpp"

// link to winsock2 static lib
#pragma comment (lib, "ws2_32.lib")

#define DEFAULT_BUFLEN 512
#define DEFAULT_PORT "22536"

class XDclientapp
{
public:
	XDclientapp(std::string& clientname, std::string& serveraddresss)
	{
		m_server_address = serveraddresss;
		m_client_name    = clientname;
		m_recvbuflen = DEFAULT_BUFLEN;
		m_isrun = true;
		m_thread = new std::thread(&XDclientapp::run, this);
	}
	~XDclientapp()
	{
		m_isrun = false;
		m_thread->join();
		delete m_thread;
	}
	void run()
	{
		WSADATA wsaData;
		SOCKET ConnectSocket = INVALID_SOCKET;
		struct addrinfo *result = NULL,
			*ptr = NULL,
			hints;
		char *sendbuf = "this is a test";
		char recvbuf[DEFAULT_BUFLEN];
		int iResult;
		int recvbuflen = DEFAULT_BUFLEN;

		// Initialize Winsock
		iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
		if (iResult != 0) {
			printf("WSAStartup failed with error: %d\n", iResult);
		}

		ZeroMemory(&hints, sizeof(hints));
		hints.ai_family = AF_UNSPEC;
		hints.ai_socktype = SOCK_STREAM;
		hints.ai_protocol = IPPROTO_TCP;

		// Resolve the server address and port
		iResult = getaddrinfo( m_server_address.c_str(), DEFAULT_PORT, &hints, &result);
		if (iResult != 0) {
			printf("getaddrinfo failed with error: %d\n", iResult);
			WSACleanup();
		}

		// Attempt to connect to an address until one succeeds
		for (ptr = result; ptr != NULL; ptr = ptr->ai_next) {

			// Create a SOCKET for connecting to server
			ConnectSocket = socket(ptr->ai_family, ptr->ai_socktype,
				ptr->ai_protocol);
			if (ConnectSocket == INVALID_SOCKET) {
				printf("socket failed with error: %ld\n", WSAGetLastError());
				WSACleanup();
			}

			// Connect to server.
			iResult = connect(ConnectSocket, ptr->ai_addr, (int)ptr->ai_addrlen);
			if (iResult == SOCKET_ERROR) {
				closesocket(ConnectSocket);
				ConnectSocket = INVALID_SOCKET;
				continue;
			}
			break;
		}
		// set to non-blocking mode
		u_long iMode = 1;
		ioctlsocket(ConnectSocket, FIONBIO, &iMode);
		freeaddrinfo(result);
		int errorid;
		while (1)
		{
			unsigned int result_recv = recv(ConnectSocket, recvbuf, recvbuflen, 0);

			errorid = WSAGetLastError();
			if (errorid != WSAEWOULDBLOCK && errorid != 0)
			{
				printf("Client disconnected: %d\n", errorid);
				break;
			}
			else if (result_recv != INVALID_SOCKET && result_recv != 0)
			{
				// printf("Bytes received: %d\n", result_recv);
				recvbuf[result_recv] = '\0';
				printf("\n%s\n", recvbuf);
				std::cout << m_client_name.c_str() << "(You) : ";
			}
			else if (result_recv == 0)
			{
				printf("Client disconnected: %d\n", errorid);
				break;
			}

			// check if there are data need to be sent
			if (!m_queue.empty())
			{
				std::string str = "";
				m_queue.try_pop(str);
				int m_recvbuflen;
				int sendresult = send( ConnectSocket , str.c_str(), str.length(), 0);
				if (sendresult == SOCKET_ERROR)
				{
					std::cout << " send failed. error code " << sendresult << std::endl;
				}
			}
			// give a sleep, avoid high percentage cpu usage
			Sleep(50);
		}
	}
	void senddata(std::string& str)
	{
		std::string pstr = m_client_name + " : " + str;
		m_queue.push(pstr);
	}
private:
	char m_recvbuf[DEFAULT_BUFLEN];
	int m_recvbuflen;
	bool m_isrun;
	std::thread* m_thread;
	concurrent_queue <std::string> m_queue;
	std::string m_client_name;
	std::string m_server_address;
};
//--
int __cdecl main(int argc,char** argv)
{
	std::string clientname = "";
	std::string serveraddress = "localhost";
	std::cout << "Please enter your name: ";
	std::getline(std::cin, clientname);

	if (argc > 1)
	{
		serveraddress = argv[1];
	}
	XDclientapp* client = new XDclientapp(clientname, serveraddress);
	std::cout << "welcome to the chat client" << std::endl;
	std::cout << "type an input to broadcast to all client. ""exit"" to close program " << std::endl;
	std::cout << "---" << std::endl;
	while (1)
	{
		std::string str_input = "";
		std::cout << clientname.c_str() << "(You) : ";
		std::getline(std::cin, str_input);

		client->senddata(str_input);
		if (!str_input.compare("exit"))
		{
			break;
		}
	}
	delete client;
	return 0;
}

Screenshoot :
chatprogram
Download : source + vs project + binary ( hanya 39 kb ! ) .

Written by XediXermawan

May 27, 2014 at 6:43 pm

Posted in C/C++ Diary

Tagged with , , ,

#define problem

leave a comment »

Masalah kecil tapi fatal di projects C/C++ yang  saya temui ( dan parahnya terulang-ulang ), adalah penggunaan define. #define secara salah digunakan karena dicampur aduk antara definisi & pengecekannya. sebagai contoh ada 2 define :

#define USE_FEATURE_A
#define USE_FEATURE_B (1)

Dalam penggunaanya ada beberapa kemungkinan :

#ifdef USE_FEATURE_A
// code_inside_a.1
#endif
–>penggunaan yang benar. code “code_inside_a.1” akan ter-compile jika USE_FEATURE_A ter-define. kalau ingin menghilangkan code “code_inside_a.1”,¬†USE_FEATURE_A harus un-define dengan komen : // #define USE_FEATURE_A

#if USE_FEATURE_A
// code_inside_a.2
#endif
–> salah. dan langsung kelihatan ( compile error ) jadi tidak perlu kawatir.

#if defined(USE_FEATURE_A)
// code_inside_a.3
#endif
–> benar. code “code_inside_a.3” akan ter-compile jika USE_FEATURE_A ter-define. kalau ingin menghilangkan code “code_inside_a.3”,¬†USE_FEATURE_A harus un-define dengan komen : // #define USE_FEATURE_A

#if USE_FEATURE_B
// code_inside_b.1
#endif
–> benar. code “code_inside_b.1” ter-compile jika #define USE_FEATURE_B (1)

#if USE_FEATURE_B == 0
// code_inside_b.2
#endif
–>benar. code “code_inside_b.2” ter-compile jika #define USE_FEATURE_B (0)

#if defined(USE_FEATURE_B)
// code_inside_b.3
#endif
->salah. walaupun “code_inside_b.3” akan ter-compile ketika #define USE_FEATURE_B (1), namun “code_inside_b.3” juga akan ter-compile jika #define USE_FEATURE_B (0)

#ifdef USE_FEATURE_B
// code_inside_b.4
#endif
–>salah. walaupun “code_inside_b.4” akan ter-compile ketika #define USE_FEATURE_B (1), namun “code_inside_b.4” juga akan ter-compile¬†jika #define USE_FEATURE_B (0)

best practice paling baik menurut saya memakai #define USE_FEATURE_B (1) dan secara konsisten menggunakan #if untuk mengecek.

 

Written by XediXermawan

May 21, 2014 at 5:22 pm

Posted in C/C++ Diary

Tagged with ,

my recent spare time projects

leave a comment »

I have been working on these projects in my spare time quite while ( like I work on it when I want work on it ¬†:p )., but recently I’m disappointed with my progress (¬†apparently¬†I’m busy and have difficulty when creating graphics assets by myself ). so I decided to upload source code to google code in case I’m interested again next time .There are 2 projects :

[1] unknown project 1. very simple game, almost done. keyword : C/C++,DirectX, Desktop App,Windows Phone 8

home project : https://code.google.com/p/my-fifteen-puzzle-dx/

svn link : https://my-fifteen-puzzle-dx.googlecode.com/svn/trunk

[2] unknown project 2 . also very simple game ( I like simple thing ). puzzle. gameplay complete. keyword : C/C++,DirectX, Desktop App, Windows Phone 8.

home project : https://code.google.com/p/proximity-game-clone2-dx/

svn link : https://proximity-game-clone2-dx.googlecode.com/svn/trunk

both of them actually same app I had made before¬†. I rewrite again these projects using entirely new c/c++ code and using DirectX as graphics API. I also implemented some design pattern there. In case you are interested , check it out ūüôā

game screenhoot running on windows phone 8 device
fiveteen_wp8

Written by XediXermawan

May 13, 2014 at 11:55 am

[3] on directx 11 : load 3D model from file

leave a comment »

di project sebelumnya saya hanya menggambar 3d kubus. sangat menarik jika program bisa me-load model 3D yang dibuat oleh 3D editor. format paling populer untuk coba-coba tentu adalah wavefront obj ūüôā . File .obj ini hanya bisa untuk static 3d model, tidak mendukung animasi. Dan biasanya ada 1 file pasangan tambahan untuk setiap .obj yaitu .mtl. ¬†File .mtl dimaksudkan untuk menyimpan material ( konstanta pencahayaan , seperti diffuse, spekular ) . namun .mtl file ini optional. File .obj sebenarnya adalah text file. terdapat 2 kata disini “3D model” & “text file”, dan pasti yang terbayang adalah parsing yang lamaaaa . dan memang iya. parsingnya lama. untuk mengakali ini , .obj diubah ke .vbo ( ada orang yang sudah buat tool converter .obj to .vbo . .vbo apa itu?, .vbo sebenarnya adalah versi binary dari .obj, sehingga tidak perlu ada proses parsing waktu load object. 1 fungsi tambahan LoadModel_VBO, dan saya mengubah informasi vertex menjadi :
DirectX::XMFLOAT3 position;
DirectX::XMFLOAT3 normal;
DirectX::XMFLOAT2 texcoord;

fungsi load VBO :

	void LoadModel_VBO( unsigned char* meshData, ID3D11Buffer** vertexBuffer,ID3D11Buffer** indexBuffer,int& vertexCount,int& indexCount)  {
		// The first 4 bytes of the BasicMesh format define the number of vertices in the mesh.
		int numVertices = *reinterpret_cast<int*>(meshData);

		// The following 4 bytes define the number of indices in the mesh.
		int numIndices = *reinterpret_cast<int*>(meshData + sizeof(int));

		// The next segment of the BasicMesh format contains the vertices of the mesh.
		vertex_type* vertices = reinterpret_cast<vertex_type*>(meshData + sizeof(int) * 2);

		// The last segment of the BasicMesh format contains the indices of the mesh.
		unsigned short* indices = reinterpret_cast<unsigned short*>(meshData + sizeof(int) * 2 + sizeof(vertex_type) * numVertices);

		// Create the vertex and index buffers with the mesh data.

		D3D11_SUBRESOURCE_DATA vertexBufferData = {0};
		vertexBufferData.pSysMem = vertices;
		vertexBufferData.SysMemPitch = 0;
		vertexBufferData.SysMemSlicePitch = 0;
		CD3D11_BUFFER_DESC vertexBufferDesc(numVertices * sizeof(vertex_type), D3D11_BIND_VERTEX_BUFFER);
		HRESULT hr = m_Device->CreateBuffer(
				&vertexBufferDesc,
				&vertexBufferData,
				vertexBuffer
				);
		assert( hr==S_OK );

		D3D11_SUBRESOURCE_DATA indexBufferData = {0};
		indexBufferData.pSysMem = indices;
		indexBufferData.SysMemPitch = 0;
		indexBufferData.SysMemSlicePitch = 0;
		CD3D11_BUFFER_DESC indexBufferDesc(numIndices * sizeof(unsigned short), D3D11_BIND_INDEX_BUFFER);
			m_Device->CreateBuffer(
				&indexBufferDesc,
				&indexBufferData,
				indexBuffer
				);
		assert( hr==S_OK );
		vertexCount = numVertices;
		indexCount = numIndices;
	}

output program ( saya me-load utah teapot –yang terkenal itu ¬†) :

teapot

full source code & project ( vs 2012 desktop ) . ( xedi_on_directx11___3___from___N.vcxproj )

svn checkout : https://xedi-on-directx-11.googlecode.com/svn/trunk

project home : http://code.google.com/p/xedi-on-directx-11/

// edi ermawan// yogyakarta , 30122013

Written by XediXermawan

December 30, 2013 at 7:01 pm

Posted in C/C++ Diary, DirectX

Tagged with ,